diff --git a/.gitmodules b/.gitmodules index 34de414594..7491ba8bcd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,23 +1,7 @@ -[submodule "modules/BasicMiddleware"] - path = modules/BasicMiddleware - url = https://github.com/aspnet/BasicMiddleware.git - branch = release/2.2 -[submodule "modules/BrowserLink"] - path = modules/BrowserLink - url = https://github.com/aspnet/BrowserLink.git - branch = release/2.2 [submodule "modules/EntityFrameworkCore"] path = modules/EntityFrameworkCore url = https://github.com/aspnet/EntityFrameworkCore.git branch = release/2.2 -[submodule "modules/Identity"] - path = modules/Identity - url = https://github.com/aspnet/Identity.git - branch = release/2.2 -[submodule "modules/Localization"] - path = modules/Localization - url = https://github.com/aspnet/Localization.git - branch = release/2.2 [submodule "modules/Scaffolding"] path = modules/Scaffolding url = https://github.com/aspnet/Scaffolding.git diff --git a/Directory.Build.props b/Directory.Build.props index cf5ad72327..b6f859c19e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -79,7 +79,7 @@ true true true - true + true false true diff --git a/Directory.Build.targets b/Directory.Build.targets index e7fc9b6812..a3e4c88b64 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -5,7 +5,7 @@ false - + diff --git a/build/artifacts.props b/build/artifacts.props index 837e529ba8..c20ee6d127 100644 --- a/build/artifacts.props +++ b/build/artifacts.props @@ -48,7 +48,6 @@ - @@ -186,8 +185,6 @@ - - diff --git a/build/buildorder.props b/build/buildorder.props index 5c986b99ca..ad4b49f520 100644 --- a/build/buildorder.props +++ b/build/buildorder.props @@ -10,7 +10,6 @@ - @@ -19,12 +18,11 @@ - - + diff --git a/build/dependencies.props b/build/dependencies.props index 73cecf140d..6e7291eae4 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -73,6 +73,8 @@ 2.2.0 2.2.0 2.2.0 + 2.2.0 + 2.2.0 2.2.0 2.2.0 2.2.0 diff --git a/build/external-dependencies.props b/build/external-dependencies.props index 8a7f7efbde..da94217a34 100644 --- a/build/external-dependencies.props +++ b/build/external-dependencies.props @@ -51,6 +51,8 @@ + + diff --git a/build/submodules.props b/build/submodules.props index 3ae5a38407..5bb1da2e42 100644 --- a/build/submodules.props +++ b/build/submodules.props @@ -51,12 +51,10 @@ - - + - diff --git a/eng/Baseline.props b/eng/Baseline.Designer.props similarity index 83% rename from eng/Baseline.props rename to eng/Baseline.Designer.props index 262c43c3ba..ee790f3217 100644 --- a/eng/Baseline.props +++ b/eng/Baseline.Designer.props @@ -152,6 +152,16 @@ + + + 2.2.0 + + + + + + + 2.2.0 @@ -242,6 +252,26 @@ + + + 2.2.0 + + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 @@ -250,6 +280,24 @@ + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + 2.2.0 @@ -266,6 +314,37 @@ + + + 2.2.0 + + + + + + + + + + + + + + + + + + + 2.2.0 + + + + + + + + + 2.2.0 diff --git a/eng/tools/BaselineGenerator/baseline.xml b/eng/Baseline.xml similarity index 87% rename from eng/tools/BaselineGenerator/baseline.xml rename to eng/Baseline.xml index e15e9099cb..8de4176b96 100644 --- a/eng/tools/BaselineGenerator/baseline.xml +++ b/eng/Baseline.xml @@ -20,6 +20,7 @@ + @@ -29,9 +30,15 @@ + + + + + + diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 200354b017..5bc9d4d3f6 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -29,10 +29,13 @@ + + + diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 92c4598018..71d2c6de7f 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -47,7 +47,14 @@ + + + + + + + diff --git a/eng/dependencies.temp.props b/eng/dependencies.temp.props index b932bd9841..57b55cd190 100644 --- a/eng/dependencies.temp.props +++ b/eng/dependencies.temp.props @@ -6,5 +6,6 @@ This is required to provide dependencies for samples and tests. + diff --git a/eng/tools/BaselineGenerator/BaselineGenerator.csproj b/eng/tools/BaselineGenerator/BaselineGenerator.csproj index 625777dbd2..8e7d5913a6 100644 --- a/eng/tools/BaselineGenerator/BaselineGenerator.csproj +++ b/eng/tools/BaselineGenerator/BaselineGenerator.csproj @@ -3,8 +3,8 @@ Exe netcoreapp2.1 - -o "$(MSBuildThisFileDirectory)../../Baseline.props" - $(MSBuildProjectDirectory) + -o "$(MSBuildThisFileDirectory)../../Baseline.Designer.props" + $(MSBuildThisFileDirectory)../../ diff --git a/eng/tools/BaselineGenerator/Program.cs b/eng/tools/BaselineGenerator/Program.cs index 689653e199..4a6177ebec 100644 --- a/eng/tools/BaselineGenerator/Program.cs +++ b/eng/tools/BaselineGenerator/Program.cs @@ -50,11 +50,11 @@ namespace PackageBaselineGenerator var tempDir = Path.Combine(Directory.GetCurrentDirectory(), "obj", "tmp"); Directory.CreateDirectory(tempDir); - var input = XDocument.Load(Path.Combine(Directory.GetCurrentDirectory(), "baseline.xml")); + var input = XDocument.Load(Path.Combine(Directory.GetCurrentDirectory(), "Baseline.xml")); var output = _output.HasValue() ? _output.Value() - : Path.Combine(Directory.GetCurrentDirectory(), "Baseline.props"); + : Path.Combine(Directory.GetCurrentDirectory(), "Baseline.Designer.props"); var baselineVersion = input.Root.Attribute("Version").Value; diff --git a/eng/tools/BaselineGenerator/README.md b/eng/tools/BaselineGenerator/README.md index 1afd97d1b5..1fd73b17f7 100644 --- a/eng/tools/BaselineGenerator/README.md +++ b/eng/tools/BaselineGenerator/README.md @@ -5,6 +5,6 @@ This tool is used to generate an MSBuild file which sets the "baseline" against ## Usage -1. Add to the [baseline.xml](./baseline.xml) a list of package ID's and their latest released versions. The source of this information can typically +1. Add to the [Baseline.xml](/eng/Baseline.xml) a list of package ID's and their latest released versions. The source of this information can typically be found in the build.xml file generated during ProdCon builds. See https://github.com/dotnet/versions/blob/master/build-info/dotnet/product/cli/release/2.1.6/build.xml for example. 2. Run `dotnet run` on this project. diff --git a/modules/BasicMiddleware b/modules/BasicMiddleware deleted file mode 160000 index 2d3ad1cb0f..0000000000 --- a/modules/BasicMiddleware +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2d3ad1cb0f9872da40c6d143ce4554563971ae04 diff --git a/modules/Identity b/modules/Identity deleted file mode 160000 index 4046c842af..0000000000 --- a/modules/Identity +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4046c842af6e3573204c2803f76e5b2a7bdbc27c diff --git a/modules/Localization b/modules/Localization deleted file mode 160000 index de1579bdda..0000000000 --- a/modules/Localization +++ /dev/null @@ -1 +0,0 @@ -Subproject commit de1579bdda645ba8631403af8720929b76939853 diff --git a/src/Hosting/test/FunctionalTests/ShutdownTests.cs b/src/Hosting/test/FunctionalTests/ShutdownTests.cs index 4249627f56..a58b1d4b63 100644 --- a/src/Hosting/test/FunctionalTests/ShutdownTests.cs +++ b/src/Hosting/test/FunctionalTests/ShutdownTests.cs @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests { var logger = loggerFactory.CreateLogger(testName); - var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("Hosting"), "test", "TestAssets", + var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("Hosting"), "test", "testassets", "Microsoft.AspNetCore.Hosting.TestSites"); var deploymentParameters = new DeploymentParameters( diff --git a/src/Hosting/test/FunctionalTests/WebHostBuilderTests.cs b/src/Hosting/test/FunctionalTests/WebHostBuilderTests.cs index f594af25b0..d1fd54f13e 100644 --- a/src/Hosting/test/FunctionalTests/WebHostBuilderTests.cs +++ b/src/Hosting/test/FunctionalTests/WebHostBuilderTests.cs @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests { var logger = loggerFactory.CreateLogger(nameof(InjectedStartup_DefaultApplicationNameIsEntryAssembly)); - var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("Hosting"), "test", "TestAssets", "IStartupInjectionAssemblyName"); + var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("Hosting"), "test", "testassets", "IStartupInjectionAssemblyName"); var deploymentParameters = new DeploymentParameters(variant) { diff --git a/src/Identity/.gitignore b/src/Identity/.gitignore new file mode 100644 index 0000000000..22d237f27e --- /dev/null +++ b/src/Identity/.gitignore @@ -0,0 +1,32 @@ +[Oo]bj/ +[Bb]in/ +TestResults/ +.nuget/ +_ReSharper.*/ +packages/ +artifacts/ +PublishProfiles/ +*.user +*.suo +*.cache +*.docstates +_ReSharper.* +nuget.exe +*net45.csproj +*net451.csproj +*k10.csproj +*.psess +*.vsp +*.pidb +*.userprefs +*DS_Store +*.ncrunchsolution +*.*sdf +*.ipch +*.sln.ide +project.lock.json +.vs +.vscode +.build/ +.testPublish/ +global.json diff --git a/src/Identity/Directory.Build.props b/src/Identity/Directory.Build.props new file mode 100644 index 0000000000..230ecfc69e --- /dev/null +++ b/src/Identity/Directory.Build.props @@ -0,0 +1,20 @@ + + + + + + + + + Microsoft ASP.NET Core + https://github.com/aspnet/Identity + git + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)build\Key.snk + true + true + + + diff --git a/src/Identity/Directory.Build.targets b/src/Identity/Directory.Build.targets new file mode 100644 index 0000000000..53b3f6e1da --- /dev/null +++ b/src/Identity/Directory.Build.targets @@ -0,0 +1,7 @@ + + + $(MicrosoftNETCoreApp20PackageVersion) + $(MicrosoftNETCoreApp21PackageVersion) + $(NETStandardLibrary20PackageVersion) + + diff --git a/src/Identity/Identity.sln b/src/Identity/Identity.sln new file mode 100644 index 0000000000..3beeb039f6 --- /dev/null +++ b/src/Identity/Identity.sln @@ -0,0 +1,482 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 15.0.26730.03 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{52D59F18-62D2-4D17-8CF2-BE192445AF8E}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity", "src\Identity\Microsoft.AspNetCore.Identity.csproj", "{1729302E-A58E-4652-B639-5B6B68DA2748}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Test", "test\Identity.Test\Microsoft.AspNetCore.Identity.Test.csproj", "{2CF3927B-19E4-4866-9BAA-2C131580E7C3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.InMemory.Test", "test\InMemory.Test\Microsoft.AspNetCore.Identity.InMemory.Test.csproj", "{65161409-C4C4-4D63-A73B-231FCFF4D503}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.Mvc", "samples\IdentitySample.Mvc\IdentitySample.Mvc.csproj", "{E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test", "test\EF.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.csproj", "{37236EA3-915D-46D5-997C-DF513C500E4B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test", "test\EF.InMemory.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj", "{EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore", "src\EF\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj", "{4490894C-3572-4E63-86F1-EE5105CE8A06}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.AspNetCoreCompat", "src\AspNetCoreCompat\Microsoft.AspNet.Identity.AspNetCoreCompat.csproj", "{6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Core", "src\Core\Microsoft.Extensions.Identity.Core.csproj", "{D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Stores", "src\Stores\Microsoft.Extensions.Identity.Stores.csproj", "{FADA11FC-DC06-4832-A569-7B2374A6CD42}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{1F83D453-E094-4D28-BCFA-9E537ABB5AD6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{42F5B772-1D61-4C18-9457-412DE8E170A5}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeWPFClient", "samples\NativeWPFClient\NativeWPFClient.csproj", "{39AA4E4D-5E62-4213-8641-BF8012D45DE4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.DefaultUI", "samples\IdentitySample.DefaultUI\IdentitySample.DefaultUI.csproj", "{ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.UI", "src\UI\Microsoft.AspNetCore.Identity.UI.csproj", "{894E102D-56D4-4B02-8F13-8781F4324C3E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.DefaultUI.WebSite", "test\WebSites\Identity.DefaultUI.WebSite\Identity.DefaultUI.WebSite.csproj", "{CAE02AD2-F941-4ACB-B469-13EFF551BB74}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.FunctionalTests", "test\Identity.FunctionalTests\Microsoft.AspNetCore.Identity.FunctionalTests.csproj", "{B3616029-7DA6-4FB3-8722-D5AC69884B3F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + DebugNoWPF|Any CPU = DebugNoWPF|Any CPU + DebugNoWPF|x64 = DebugNoWPF|x64 + DebugNoWPF|x86 = DebugNoWPF|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseNoWPF|Any CPU = ReleaseNoWPF|Any CPU + ReleaseNoWPF|x64 = ReleaseNoWPF|x64 + ReleaseNoWPF|x86 = ReleaseNoWPF|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x86.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x86.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x86.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x86.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x86.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x86.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x86.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x86.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x86.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x86.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x86.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x86.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x86.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x86.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x86.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x86.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x86.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x86.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x86.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x86.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x86.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x86.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x64.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x64.Build.0 = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x86.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Debug|x86.Build.0 = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|Any CPU.Build.0 = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x64.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x64.Build.0 = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x86.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.Release|x86.Build.0 = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|x64.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|x64.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|x86.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Debug|x86.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|Any CPU.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|x64.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|x64.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|x86.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.Release|x86.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|x64.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|x64.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|x86.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Debug|x86.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|Any CPU.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|x64.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|x64.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|x86.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.Release|x86.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|x64.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|x64.Build.0 = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|x86.ActiveCfg = Release|Any CPU + {894E102D-56D4-4B02-8F13-8781F4324C3E}.ReleaseNoWPF|x86.Build.0 = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|x64.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|x64.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|x86.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Debug|x86.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|Any CPU.Build.0 = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x64.ActiveCfg = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x64.Build.0 = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x86.ActiveCfg = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.Release|x86.Build.0 = Release|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|Any CPU.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU + {CAE02AD2-F941-4ACB-B469-13EFF551BB74}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|x64.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Debug|x86.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|Any CPU.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|Any CPU.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|x64.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|x64.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|x86.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.DebugNoWPF|x86.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|Any CPU.Build.0 = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|x64.ActiveCfg = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|x64.Build.0 = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|x86.ActiveCfg = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.Release|x86.Build.0 = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|Any CPU.ActiveCfg = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|Any CPU.Build.0 = Release|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x64.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x64.Build.0 = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x86.ActiveCfg = Debug|Any CPU + {B3616029-7DA6-4FB3-8722-D5AC69884B3F}.ReleaseNoWPF|x86.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {2CF3927B-19E4-4866-9BAA-2C131580E7C3} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50} + {37236EA3-915D-46D5-997C-DF513C500E4B} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {4490894C-3572-4E63-86F1-EE5105CE8A06} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {5608E828-DD54-4E2A-B73C-FC22268BE797} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {FADA11FC-DC06-4832-A569-7B2374A6CD42} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {1F83D453-E094-4D28-BCFA-9E537ABB5AD6} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {39AA4E4D-5E62-4213-8641-BF8012D45DE4} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50} + {ACC75F4F-EA7D-49E0-A64C-9D4A3DFD5B8A} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50} + {894E102D-56D4-4B02-8F13-8781F4324C3E} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {CAE02AD2-F941-4ACB-B469-13EFF551BB74} = {1F83D453-E094-4D28-BCFA-9E537ABB5AD6} + {B3616029-7DA6-4FB3-8722-D5AC69884B3F} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B3F2A592-CCE0-40C2-8CA4-7B1293DED874} + EndGlobalSection +EndGlobal diff --git a/src/Identity/IdentityCore.sln b/src/Identity/IdentityCore.sln new file mode 100644 index 0000000000..3971ae00b5 --- /dev/null +++ b/src/Identity/IdentityCore.sln @@ -0,0 +1,319 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.10 +MinimumVisualStudioVersion = 15.0.26730.03 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{52D59F18-62D2-4D17-8CF2-BE192445AF8E}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity", "src\Identity\Microsoft.AspNetCore.Identity.csproj", "{1729302E-A58E-4652-B639-5B6B68DA2748}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Test", "test\Identity.Test\Microsoft.AspNetCore.Identity.Test.csproj", "{2CF3927B-19E4-4866-9BAA-2C131580E7C3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.InMemory.Test", "test\InMemory.Test\Microsoft.AspNetCore.Identity.InMemory.Test.csproj", "{65161409-C4C4-4D63-A73B-231FCFF4D503}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{58D94A0E-C2B7-43A7-8826-99ECBB1E0A50}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.Mvc", "samples\IdentitySample.Mvc\IdentitySample.Mvc.csproj", "{E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test", "test\EF.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.csproj", "{37236EA3-915D-46D5-997C-DF513C500E4B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test", "test\EF.InMemory.Test\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj", "{EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.EntityFrameworkCore", "src\EF\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj", "{4490894C-3572-4E63-86F1-EE5105CE8A06}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.AspNetCoreCompat", "src\AspNetCoreCompat\Microsoft.AspNet.Identity.AspNetCoreCompat.csproj", "{6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Core", "src\Core\Microsoft.Extensions.Identity.Core.csproj", "{D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Identity.Stores", "src\Stores\Microsoft.Extensions.Identity.Stores.csproj", "{FADA11FC-DC06-4832-A569-7B2374A6CD42}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2C657F6C-D8AD-4833-9C59-2301A16957BD}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentitySample.DefaultUI", "samples\IdentitySample.DefaultUI\IdentitySample.DefaultUI.csproj", "{D5FB2E24-4C71-430C-A289-59C8D59164B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.UI", "src\UI\Microsoft.AspNetCore.Identity.UI.csproj", "{1FB3E9BB-E20A-4807-A4C3-F86A341304DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.FunctionalTests", "test\Identity.FunctionalTests\Microsoft.AspNetCore.Identity.FunctionalTests.csproj", "{BAC36757-9A47-43CB-A6F3-86E8C4650A28}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.DefaultUI.WebSite", "test\WebSites\Identity.DefaultUI.WebSite\Identity.DefaultUI.WebSite.csproj", "{EA424B4D-0BE1-49AC-A106-CC6CC808A104}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x64.Build.0 = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|x86.ActiveCfg = Debug|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Any CPU.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.ActiveCfg = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x64.Build.0 = Release|Any CPU + {1729302E-A58E-4652-B639-5B6B68DA2748}.Release|x86.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x64.Build.0 = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Any CPU.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.ActiveCfg = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x64.Build.0 = Release|Any CPU + {2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x64.Build.0 = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|x86.ActiveCfg = Debug|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Any CPU.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.ActiveCfg = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x64.Build.0 = Release|Any CPU + {65161409-C4C4-4D63-A73B-231FCFF4D503}.Release|x86.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x64.Build.0 = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Any CPU.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.ActiveCfg = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x64.Build.0 = Release|Any CPU + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6}.Release|x86.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x64.Build.0 = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Any CPU.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.ActiveCfg = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x64.Build.0 = Release|Any CPU + {37236EA3-915D-46D5-997C-DF513C500E4B}.Release|x86.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x64.Build.0 = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Any CPU.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.ActiveCfg = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x64.Build.0 = Release|Any CPU + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD}.Release|x86.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x64.Build.0 = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Debug|x86.ActiveCfg = Debug|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Any CPU.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.ActiveCfg = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x64.Build.0 = Release|Any CPU + {4490894C-3572-4E63-86F1-EE5105CE8A06}.Release|x86.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x64.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.ActiveCfg = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Debug|x86.Build.0 = Debug|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Any CPU.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x64.Build.0 = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.ActiveCfg = Release|Any CPU + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475}.Release|x86.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x64.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.ActiveCfg = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Debug|x86.Build.0 = Debug|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Any CPU.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x64.Build.0 = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.ActiveCfg = Release|Any CPU + {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x64.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Debug|x86.Build.0 = Debug|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Any CPU.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x64.Build.0 = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.ActiveCfg = Release|Any CPU + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8}.Release|x86.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x64.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.ActiveCfg = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Debug|x86.Build.0 = Debug|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Any CPU.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x64.Build.0 = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.ActiveCfg = Release|Any CPU + {FADA11FC-DC06-4832-A569-7B2374A6CD42}.Release|x86.Build.0 = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x64.Build.0 = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Debug|x86.Build.0 = Debug|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Any CPU.Build.0 = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x64.ActiveCfg = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x64.Build.0 = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x86.ActiveCfg = Release|Any CPU + {D5FB2E24-4C71-430C-A289-59C8D59164B0}.Release|x86.Build.0 = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x64.Build.0 = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x86.ActiveCfg = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Debug|x86.Build.0 = Debug|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Any CPU.Build.0 = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x64.ActiveCfg = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x64.Build.0 = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x86.ActiveCfg = Release|Any CPU + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB}.Release|x86.Build.0 = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x64.Build.0 = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Debug|x86.Build.0 = Debug|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Any CPU.Build.0 = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x64.ActiveCfg = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x64.Build.0 = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x86.ActiveCfg = Release|Any CPU + {BAC36757-9A47-43CB-A6F3-86E8C4650A28}.Release|x86.Build.0 = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x64.Build.0 = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Debug|x86.Build.0 = Debug|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Any CPU.Build.0 = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x64.ActiveCfg = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x64.Build.0 = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x86.ActiveCfg = Release|Any CPU + {EA424B4D-0BE1-49AC-A106-CC6CC808A104}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {2CF3927B-19E4-4866-9BAA-2C131580E7C3} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {E1BFA023-CFFD-49CE-8466-1C28DD2EC1F6} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50} + {37236EA3-915D-46D5-997C-DF513C500E4B} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {EA7EB28F-53B8-4009-9C6B-74DB090CA8DD} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {4490894C-3572-4E63-86F1-EE5105CE8A06} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {5608E828-DD54-4E2A-B73C-FC22268BE797} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {D5905D78-A32E-44B8-8F21-EDAEDC95D9B8} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {FADA11FC-DC06-4832-A569-7B2374A6CD42} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {D5FB2E24-4C71-430C-A289-59C8D59164B0} = {58D94A0E-C2B7-43A7-8826-99ECBB1E0A50} + {1FB3E9BB-E20A-4807-A4C3-F86A341304DB} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {BAC36757-9A47-43CB-A6F3-86E8C4650A28} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {EA424B4D-0BE1-49AC-A106-CC6CC808A104} = {C47C1165-9F19-4DF8-ABA9-707ACEB3BDC7} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {21D598B0-2383-4B22-826D-E7FB4921BD66} + EndGlobalSection +EndGlobal diff --git a/src/Identity/NuGetPackageVerifier.json b/src/Identity/NuGetPackageVerifier.json new file mode 100644 index 0000000000..df04266b02 --- /dev/null +++ b/src/Identity/NuGetPackageVerifier.json @@ -0,0 +1,17 @@ +{ + "Default": { + "rules": [ + "DefaultCompositeRule" + ], + "packages": { + "Microsoft.AspNetCore.Identity.UI": { + "Exclusions": { + "DOC_MISSING": { + "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll": "This is a library with packaged UI", + "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.dll": "This assembly is generated and contains precompiled razor pages" + } + } + } + } + } +} diff --git a/src/Identity/README.md b/src/Identity/README.md new file mode 100644 index 0000000000..4e4f345af9 --- /dev/null +++ b/src/Identity/README.md @@ -0,0 +1,30 @@ +ASP.NET Core Identity +=== + +AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/vf79kttspnblh2hx/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/Identity/branch/dev) + +Travis: [![Travis](https://travis-ci.org/aspnet/Identity.svg?branch=dev)](https://travis-ci.org/aspnet/Identity) + +ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. ASP.NET Core Identity allows you to add login features to your application and makes it easy to customize data about the logged in user. + +This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. + +## ASP.NET Identity for ASP.NET MVC 5 + +The previous versions of Identity for MVC5 and lower, previously available on CodePlex, are available at https://github.com/aspnet/AspNetIdentity + +## Community Maintained Store Providers + +**IMPORTANT: Extensions are built by a variety of sources and not maintained as part of the ASP.NET Identity project. When considering a third party provider, be sure to evaluate quality, licensing, compatibility, support, etc. to ensure they meet your requirements.** + +* ASP.NET Identity MongoDB Providers: + * [By Tugberk Ugurlu](https://github.com/tugberkugurlu/AspNetCore.Identity.MongoDB) + * [By Alexandre Spieser](https://github.com/alexandre-spieser/AspNetCore.Identity.MongoDbCore) + * [ASP.NET Identity LinqToDB Provider](https://github.com/ili/LinqToDB.Identity) + * [ASP.NET Identity DynamoDB Provider](https://github.com/miltador/AspNetCore.Identity.DynamoDB) + * ASP.NET Identity RavenDB Providers: + * [By Judah Gabriel Himango](https://github.com/JudahGabriel/RavenDB.Identity) + * [By Iskandar Rafiev](https://github.com/maqduni/AspNetCore.Identity.RavenDB) + * [ASP.NET Identity Cassandra Provider](https://github.com/lkubis/AspNetCore.Identity.Cassandra) + * [ASP.NET Identity Firebase Provider](https://github.com/aguacongas/Identity.Firebase) + * [ASP.NET Identity Redis Provider](https://github.com/aguacongas/Identity.Redis) diff --git a/src/Identity/build/Key.snk b/src/Identity/build/Key.snk new file mode 100644 index 0000000000..e10e4889c1 Binary files /dev/null and b/src/Identity/build/Key.snk differ diff --git a/src/Identity/build/dependencies.props b/src/Identity/build/dependencies.props new file mode 100644 index 0000000000..21e3cb41e5 --- /dev/null +++ b/src/Identity/build/dependencies.props @@ -0,0 +1,81 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + 0.9.9 + 2.1.3-rtm-15831 + 2.2.1 + 2.0.0 + 2.1.5 + 15.6.1 + 3.0.1 + 4.7.49 + 2.0.3 + 4.5.0 + 0.8.0 + 2.3.1 + 2.4.0 + + + + + + + + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.2 + 2.1.2 + 2.1.1 + 2.1.1 + 2.1.0 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + 2.1.1 + + diff --git a/src/Identity/build/repo.props b/src/Identity/build/repo.props new file mode 100644 index 0000000000..b4c0d5f738 --- /dev/null +++ b/src/Identity/build/repo.props @@ -0,0 +1,19 @@ + + + + + + + + + + Internal.AspNetCore.Universe.Lineup + 2.1.0-rc1-* + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json + + + + + + + diff --git a/src/Identity/build/repo.targets b/src/Identity/build/repo.targets new file mode 100644 index 0000000000..867ea2537a --- /dev/null +++ b/src/Identity/build/repo.targets @@ -0,0 +1,8 @@ + + + + + Configuration=$(Configuration)NoWPF + + + diff --git a/src/Identity/build/sources.props b/src/Identity/build/sources.props new file mode 100644 index 0000000000..9215df9751 --- /dev/null +++ b/src/Identity/build/sources.props @@ -0,0 +1,17 @@ + + + + + $(DotNetRestoreSources) + + $(RestoreSources); + https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; + + + $(RestoreSources); + https://api.nuget.org/v3/index.json; + + + diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml new file mode 100644 index 0000000000..3f15aaca23 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -0,0 +1,53 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Profile"; +} + +

@ViewData["Title"]

+@Html.Partial("_StatusMessage", Model.StatusMessage) +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + @if (Model.IsEmailConfirmed) + { +
+ + +
+} +else +{ + + +} + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 0000000000..23303774f0 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,165 @@ +// 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.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using IdentitySample.DefaultUI.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace IdentitySample.DefaultUI +{ + public class IndexModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + + public IndexModel( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + } + + public string Username { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + + [Required] + [DataType(DataType.Text)] + [Display(Name = "Full Name")] + public string Name { get; set; } + + [Required] + [Range(0, 199, ErrorMessage = "Age must be between 0 and 199")] + [Display(Name = "Age")] + public int Age { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + Username = user.UserName; + Input = new InputModel + { + Name = user.Name, + Age = user.Age, + Email = user.Email, + PhoneNumber = user.PhoneNumber + }; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + + return Page(); + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (Input.Name != user.Name) + { + user.Name = Input.Name; + } + + if (Input.Age != user.Age) + { + user.Age = Input.Age; + } + + var updateProfileResult = await _userManager.UpdateAsync(user); + if (!updateProfileResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error ocurred updating the profile for user with ID '{user.Id}'"); + } + + if (Input.Email != user.Email) + { + var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); + if (!setEmailResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{user.Id}'."); + } + } + + if (Input.PhoneNumber != user.PhoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'."); + } + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + return RedirectToPage(); + } + + public async Task OnPostSendVerificationEmailAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { user.Id, code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + user.Email, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml new file mode 100644 index 0000000000..259373e966 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml @@ -0,0 +1,47 @@ +@page +@model RegisterModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+ +
+
+
+

Create a new account.

+
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml.cs new file mode 100644 index 0000000000..35cb402bb8 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -0,0 +1,115 @@ +// 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.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using IdentitySample.DefaultUI.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace IdentitySample.DefaultUI +{ + public class RegisterModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + private readonly IEmailSender _emailSender; + + public RegisterModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + [Required] + [DataType(DataType.Text)] + [Display(Name = "Full name")] + public string Name { get; set; } + + [Required] + [Range(0,199, ErrorMessage = "Age must be between 0 and 199 years")] + [Display(Name = "Age")] + public int Age { get; set; } + } + + public void OnGet(string returnUrl = null) + { + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if (ModelState.IsValid) + { + var user = new ApplicationUser { + UserName = Input.Email, + Email = Input.Email, + Name = Input.Name, + Age = Input.Age + }; + + var result = await _userManager.CreateAsync(user, Input.Password); + if (result.Succeeded) + { + _logger.LogInformation("User created a new account with password."); + + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = user.Id, code = code }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + await _signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewImports.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewImports.cshtml new file mode 100644 index 0000000000..777abd9e19 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@namespace IdentitySample.DefaultUI +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml new file mode 100644 index 0000000000..c4284f6c20 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "/Views/Shared/_Layout.cshtml"; +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Controllers/HomeController.cs b/src/Identity/samples/IdentitySample.DefaultUI/Controllers/HomeController.cs new file mode 100644 index 0000000000..d0883b03d3 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Controllers/HomeController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; + +namespace IdentitySample.DefaultUI.Controllers +{ + public class HomeController : Controller + { + [HttpGet] + public IActionResult Index() + { + return View(); + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationDbContext.cs b/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationDbContext.cs new file mode 100644 index 0000000000..1e96af6bee --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationDbContext.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace IdentitySample.DefaultUI.Data +{ + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(DbContextOptions options) : base(options) + { + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationUser.cs b/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationUser.cs new file mode 100644 index 0000000000..cba3ed5f40 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Data/ApplicationUser.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Identity; + +namespace IdentitySample.DefaultUI.Data +{ + public class ApplicationUser : IdentityUser + { + [ProtectedPersonalData] + public string Name { get; set; } + [PersonalData] + public int Age { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.Designer.cs b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..acee5cb362 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.Designer.cs @@ -0,0 +1,239 @@ +// +using System; +using IdentitySample.DefaultUI.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; + +namespace IdentitySample.DefaultUI.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20180126174859_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-preview1-28153") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("IdentitySample.DefaultUI.Data.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Age"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("Name"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.cs b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.cs new file mode 100644 index 0000000000..713e581f6d --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/20180126174859_CreateIdentitySchema.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace IdentitySample.DefaultUI.Data.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + 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) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + 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), + Name = table.Column(nullable: true), + Age = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + UserId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + 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) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + 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) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..8eb1302cd7 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,238 @@ +// +using System; +using IdentitySample.DefaultUI.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; + +namespace IdentitySample.DefaultUI.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.0-preview1-28153") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("IdentitySample.DefaultUI.Data.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Age"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("Name"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("IdentitySample.DefaultUI.Data.ApplicationUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj b/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj new file mode 100644 index 0000000000..43b55a62d9 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj @@ -0,0 +1,35 @@ + + + + Identity sample MVC application on ASP.NET Core using the default UI + netcoreapp2.1;net461 + aspnetcore-2ff9bc27-5e8c-4484-90ca-e3aace89b72a + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Program.cs b/src/Identity/samples/IdentitySample.DefaultUI/Program.cs new file mode 100644 index 0000000000..6a8f2a7048 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Program.cs @@ -0,0 +1,23 @@ +using System.IO; +using Microsoft.AspNetCore.Hosting; + +namespace IdentitySample.DefaultUI +{ + public static class Program + { + public static void Main(string[] args) + { + var host = CreateWebHostBuilder(args) + .Build(); + + host.Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup(); + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Properties/launchSettings.json b/src/Identity/samples/IdentitySample.DefaultUI/Properties/launchSettings.json new file mode 100644 index 0000000000..cef8aeebde --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:28248/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "web": { + "commandName": "web", + "environmentVariables": { + "ASPNET_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Startup.cs b/src/Identity/samples/IdentitySample.DefaultUI/Startup.cs new file mode 100644 index 0000000000..76962038f4 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Startup.cs @@ -0,0 +1,68 @@ +using IdentitySample.DefaultUI.Data; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace IdentitySample.DefaultUI +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + + builder.AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; set; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add framework services. + services.AddDbContext( + options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), + x => x.MigrationsAssembly("IdentitySample.DefaultUI"))); + + services.AddMvc(); + + services.AddDefaultIdentity() + .AddRoles() + .AddEntityFrameworkStores(); + } + + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseDatabaseErrorPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseAuthentication(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/Home/Index.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/Home/Index.cshtml new file mode 100644 index 0000000000..59dc45d93a --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/Home/Index.cshtml @@ -0,0 +1,233 @@ +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + ViewBag.Title = "Home Page"; +} + +
+

ASP.NET Identity

+

ASP.NET Identity is the membership system for ASP.NET apps. Following are the features of ASP.NET Identity in this sample application.

+

Learn more »

+
+ +
+
+
+
Initialize ASP.NET Identity
+
+ You can initialize ASP.NET Identity when the application starts. Since ASP.NET Identity is Entity Framework based in this sample, + you can create DatabaseInitializer which is configured to get called each time the app starts. + Please look in App_Start\IdentityConfig.cs + This code shows the following +
    +
  • When should the Initializer run and when should the database be created
  • +
  • Create Admin user
  • +
  • Create Admin role
  • +
  • Add Admin user to Admin role
  • +
+
+
+
+
+
+
Add profile data for the user
+
+ Please follow this tutorial. + +
    +
  • Add profile information in the Users Table
  • +
  • Look in Models\IdentityModels.cs for examples
  • +
+
+
+
+
+
+
Validation
+
+ When you create a User using a username or password, the Identity system performs validation on the username and password, and the passwords are hashed before they are + stored in the database. You can customize the validation by changing some of the properties of the validators such as Turn alphanumeric on/off, set minimum password length + or you can write your own custom validators and register them with the UserManager. +
+
+
+
+
+
Register a user and login
+
+ Click @Html.ActionLink("Register", "Register", "Account") and see the code in AccountController.cs and Register Action. + Click @Html.ActionLink("Log in", "Login", "Account") and see the code in AccountController.cs and Login Action. +
+
+
+
+
+
Social Logins
+
+ You can the support so that users can login using their Facebook, Google, Twitter, Microsoft Account and more. +
+
+ +
+
+
+
+
+
Basic User Management
+
+ Do Create, Update, List and Delete Users. + Assign a Role to a User. + Only Users In Role Admin can access this page. This uses the [Authorize(Roles = "Admin")] on the UserAdmin controller. +
+
+
+
+
+
Basic Role Management
+
+ Do Create, Update, List and Delete Roles. + Only Users In Role Admin can access this page. This authorization is done by using the [Authorize(Roles = "Admin")] on the RolesAdmin controller. +
+
+
+
+
+
Account Confirmation
+
+ When you register a new account, you will be sent an email confirmation. + You can use an email service such as SendGrid which integrates nicely with Windows Azure and requires no configuration or + set up an SMTP server to send email. + You can send email using the EmailService which is registered in App_Start\IdentityConfig.cs +
+
+
+
+
+
Two-Factor Authentication
+
+ This sample shows how you can use Two-Factor authentication. This sample has a SMS and email service registered where you can send SMS or email for sending the security code. + You can add more two-factor authentication factors such as QR codes and plug them into ASP.NET Identity. +
    +
  • + You can use a SMS using Twilio or use any means of sending SMS. Please read for more details on using Twilio. + You can send SMS using the SmsService which is registered in App_Start\IdentityConfig.cs +
  • +
  • + You can use an email service such as SendGrid or + set up an SMTP server to send email. + You can send email using the EmailService which is registered in App_Start\IdentityConfig.cs +
  • + +
  • + When you login, you can add a phone number by clicking the Manage page. +
  • +
  • + Once you add a phone number and have the Phone service hooked to send a SMS, you will get a code through SMS to confirm your phone number. +
  • +
  • + In the Manage page, you can turn on Two-Factor authentication. +
  • +
  • + When you logout and login, after you enter the username and password, you will get an option of how to get the security code to use for two-factor authentication. +
  • +
  • + You can copy the code from your SMS or email and enter in the form to login. +
  • +
  • + The sample also shows how to protect against Brute force attacks against two-factor codes. When you enter a code incorrectly for 5 times then you will be + lockedout for 5 min before you can enter a new code. These settings can be configured in App_Start\IdentityConfig.cs by setting DefaultAccountLockoutTimeSpan and MaxFailedAccessAttemptsBeforeLockout on the UserManager. +
  • +
  • + If the machine you are browsing this website is your own machine, you can choose to check the "Remember Me" option after you enter the code. + This option will remember you forever on this machine and will not ask you for the two-factor authentication, the next time when you login to the website. + You can change your "Remember Me" settings for two-factor authentication in the Manage page. +
  • +
+
+
+
+
+
+
Account Lockout
+
+ Provide a way to Lockout out the user if the user enters their password or two-factor codes incorrectly. + The number of invalid attempts and the timespan for the users are locked out can be configured. + A developer can optionally turn off Account Lockout for certain user accounts should they need to. +
+
    +
  • Account LockOut settings can be configured in the UserManager in IdentityConfig.cs
  • +
+
+
+
+
+
Security Token provider
+
+ Support a way to regenerate the Security Token for the user in cases when the User changes there password or any other security related information such as removing an associated login(such as Facebook, Google, Microsoft Account etc). + This is needed to ensure that any tokens generated with the old password are invalidated. In the sample project, if you change the users password then a new token is generated for the user and any previous tokens are invalidated. + This feature provides an extra layer of security to your application since when you change your password, you will be logged out from everywhere (all other browsers) where you have logged into this application. +
+
+
    +
  • The provider is registered when you add CookieAuthentication in StartupAuth to your application.
  • +
+
+
+
+
+
+
Password Reset
+
+ Allows the user to reset their passwords if they have forgotten their password. In this sample users need to confirm their email before they can reset their passwords. +
+
+
+
+
+
Custom Storage providers
+
+ You can extend ASP.NET Identity to write your own custom storage provider for storing the ASP.NET Identity system and user data + in a persistance system of your choice such as MondoDb, RavenDb, Azure Table Storage etc. +
+
+ +
+
+
+
+
+
Documentation
+
+ +
+
+
+
diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_Layout.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..d0d0d86988 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_Layout.cshtml @@ -0,0 +1,44 @@ + + + + + + @ViewData["Title"] - Identity Sample + + + + + +
+ @RenderBody() +
+
+

© 2018 - IdentitySample.DefaultUI

+
+
+ + + + + + @RenderSection("scripts", required: false) + + diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_LoginPartial.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 0000000000..3e8d8f66d3 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,24 @@ +@using IdentitySample.DefaultUI.Data +@inject SignInManager SignInManager +@inject UserManager UserManager + +@if (SignInManager.IsSignedIn(User)) +{ + +} +else +{ + +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_ValidationScriptsPartial.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000000..a6c30d4d82 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/Shared/_ValidationScriptsPartial.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewImports.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..cca35071d9 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using Microsoft.AspNetCore.Identity +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewStart.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..a5f10045db --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/appsettings.json b/src/Identity/samples/IdentitySample.DefaultUI/appsettings.json new file mode 100644 index 0000000000..f781a53564 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/appsettings.json @@ -0,0 +1,13 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-IdentitySample.DefaultUI-47781151-7d38-4b7b-8fe4-9a8b299f124f;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/web.Debug.config b/src/Identity/samples/IdentitySample.DefaultUI/web.Debug.config new file mode 100644 index 0000000000..2e302f9f95 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/web.Release.config b/src/Identity/samples/IdentitySample.DefaultUI/web.Release.config new file mode 100644 index 0000000000..c35844462b --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/web.config b/src/Identity/samples/IdentitySample.DefaultUI/web.config new file mode 100644 index 0000000000..f7ac679334 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/web.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.css b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.css new file mode 100644 index 0000000000..6d0f6e44ec --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.css @@ -0,0 +1,35 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Wrapping element */ +/* Set some basic padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Carousel */ +.carousel-caption p { + font-size: 20px; + line-height: 1.4; +} + +/* Make .svg files in the carousel display properly in older browsers */ +.carousel-inner .item img[src$=".svg"] { + width: 100%; +} + +/* QR code generator */ +#qrCode { + margin: 15px; +} + +/* Hide/rearrange for smaller screens */ +@media screen and (max-width: 767px) { + /* Hide captions */ + .carousel-caption { + display: none; + } +} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.min.css b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.min.css new file mode 100644 index 0000000000..5e93e30ae3 --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/css/site.min.css @@ -0,0 +1 @@ +body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/favicon.ico b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/favicon.ico differ diff --git a/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/js/site.js b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/js/site.js new file mode 100644 index 0000000000..0f3411a45b --- /dev/null +++ b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/js/site.js @@ -0,0 +1 @@ +// Write your JavaScript code. diff --git a/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/js/site.min.js b/src/Identity/samples/IdentitySample.DefaultUI/wwwroot/js/site.min.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs b/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs new file mode 100644 index 0000000000..ca996ea292 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs @@ -0,0 +1,565 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentitySample.Models; +using IdentitySample.Models.AccountViewModels; +using IdentitySample.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Logging; + +namespace IdentitySample.Controllers +{ + [Authorize] + public class AccountController : Controller + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly ISmsSender _smsSender; + private readonly ILogger _logger; + + public AccountController( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender, + ISmsSender smsSender, + ILoggerFactory loggerFactory) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _smsSender = smsSender; + _logger = loggerFactory.CreateLogger(); + } + + // + // GET: /Account/Login + [HttpGet] + [AllowAnonymous] + public IActionResult Login(string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + // + // POST: /Account/Login + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Login(LoginViewModel model, string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); + if (result.Succeeded) + { + _logger.LogInformation(1, "User logged in."); + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning(2, "User account locked out."); + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return View(model); + } + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/Register + [HttpGet] + [AllowAnonymous] + public IActionResult Register(string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + // + // POST: /Account/Register + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task Register(RegisterViewModel model, string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + if (ModelState.IsValid) + { + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await _userManager.CreateAsync(user, model.Password); + if (result.Succeeded) + { + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); + //await _emailSender.SendEmailAsync(model.Email, "Confirm your account", + // "Please confirm your account by clicking this link: link"); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(3, "User created a new account with password."); + return RedirectToLocal(returnUrl); + } + AddErrors(result); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // POST: /Account/LogOff + [HttpPost] + [ValidateAntiForgeryToken] + public async Task LogOff() + { + await _signInManager.SignOutAsync(); + _logger.LogInformation(4, "User logged out."); + return RedirectToAction(nameof(HomeController.Index), "Home"); + } + + // + // POST: /Account/ExternalLogin + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public IActionResult ExternalLogin(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return Challenge(properties, provider); + } + + // + // GET: /Account/ExternalLoginCallback + [HttpGet] + [AllowAnonymous] + public async Task ExternalLoginCallback(string returnUrl = null, string remoteError = null) + { + if (remoteError != null) + { + ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}"); + return View(nameof(Login)); + } + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return RedirectToAction(nameof(Login)); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); + if (result.Succeeded) + { + // Update any authentication tokens if login succeeded + await _signInManager.UpdateExternalAuthenticationTokensAsync(info); + + _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider); + return RedirectToLocal(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl }); + } + if (result.IsLockedOut) + { + return View("Lockout"); + } + else + { + // If the user does not have an account, then ask the user to create an account. + ViewData["ReturnUrl"] = returnUrl; + ViewData["LoginProvider"] = info.LoginProvider; + var email = info.Principal.FindFirstValue(ClaimTypes.Email); + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email }); + } + } + + // + // POST: /Account/ExternalLoginConfirmation + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) + { + if (ModelState.IsValid) + { + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return View("ExternalLoginFailure"); + } + var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; + var result = await _userManager.CreateAsync(user); + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider); + + // Update any authentication tokens as well + await _signInManager.UpdateExternalAuthenticationTokensAsync(info); + + return RedirectToLocal(returnUrl); + } + } + AddErrors(result); + } + + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } + + // GET: /Account/ConfirmEmail + [HttpGet] + [AllowAnonymous] + public async Task ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } + var result = await _userManager.ConfirmEmailAsync(user, code); + return View(result.Succeeded ? "ConfirmEmail" : "Error"); + } + + // + // GET: /Account/ForgotPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPassword() + { + return View(); + } + + // + // POST: /Account/ForgotPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ForgotPassword(ForgotPasswordViewModel model) + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(model.Email); + if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return View("ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713 + // Send an email with this link + //var code = await _userManager.GeneratePasswordResetTokenAsync(user); + //var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); + //await _emailSender.SendEmailAsync(model.Email, "Reset Password", + // "Please reset your password by clicking here: link"); + //return View("ForgotPasswordConfirmation"); + } + + // If we got this far, something failed, redisplay form + return View(model); + } + + // + // GET: /Account/ForgotPasswordConfirmation + [HttpGet] + [AllowAnonymous] + public IActionResult ForgotPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/ResetPassword + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPassword(string code = null) + { + return code == null ? View("Error") : View(); + } + + // + // POST: /Account/ResetPassword + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task ResetPassword(ResetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await _userManager.FindByEmailAsync(model.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account"); + } + var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password); + if (result.Succeeded) + { + return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account"); + } + AddErrors(result); + return View(); + } + + // + // GET: /Account/ResetPasswordConfirmation + [HttpGet] + [AllowAnonymous] + public IActionResult ResetPasswordConfirmation() + { + return View(); + } + + // + // GET: /Account/SendCode + [HttpGet] + [AllowAnonymous] + public async Task SendCode(string returnUrl = null, bool rememberMe = false) + { + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); + var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); + return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/SendCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task SendCode(SendCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + + if (model.SelectedProvider == "Authenticator") + { + return RedirectToAction(nameof(VerifyAuthenticatorCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + } + + // Generate the token and send it + var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); + if (string.IsNullOrWhiteSpace(code)) + { + return View("Error"); + } + + var message = "Your security code is: " + code; + if (model.SelectedProvider == "Email") + { + await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message); + } + else if (model.SelectedProvider == "Phone") + { + await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message); + } + + return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); + } + + // + // GET: /Account/VerifyCode + [HttpGet] + [AllowAnonymous] + public async Task VerifyCode(string provider, bool rememberMe, string returnUrl = null) + { + // Require that the user has already logged in via username/password or external login + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/VerifyCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task VerifyCode(VerifyCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + // The following code protects for brute force attacks against the two factor codes. + // If a user enters incorrect codes for a specified amount of time then the user account + // will be locked out for a specified amount of time. + var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); + if (result.Succeeded) + { + return RedirectToLocal(model.ReturnUrl); + } + if (result.IsLockedOut) + { + _logger.LogWarning(7, "User account locked out."); + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid code."); + return View(model); + } + } + + // + // GET: /Account/VerifyAuthenticatorCode + [HttpGet] + [AllowAnonymous] + public async Task VerifyAuthenticatorCode(bool rememberMe, string returnUrl = null) + { + // Require that the user has already logged in via username/password or external login + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + return View(new VerifyAuthenticatorCodeViewModel { ReturnUrl = returnUrl, RememberMe = rememberMe }); + } + + // + // POST: /Account/VerifyAuthenticatorCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task VerifyAuthenticatorCode(VerifyAuthenticatorCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + // The following code protects for brute force attacks against the two factor codes. + // If a user enters incorrect codes for a specified amount of time then the user account + // will be locked out for a specified amount of time. + var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(model.Code, model.RememberMe, model.RememberBrowser); + if (result.Succeeded) + { + return RedirectToLocal(model.ReturnUrl); + } + if (result.IsLockedOut) + { + _logger.LogWarning(7, "User account locked out."); + return View("Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid code."); + return View(model); + } + } + + // + // GET: /Account/UseRecoveryCode + [HttpGet] + [AllowAnonymous] + public async Task UseRecoveryCode(string returnUrl = null) + { + // Require that the user has already logged in via username/password or external login + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + return View("Error"); + } + return View(new UseRecoveryCodeViewModel { ReturnUrl = returnUrl }); + } + + // + // POST: /Account/UseRecoveryCode + [HttpPost] + [AllowAnonymous] + [ValidateAntiForgeryToken] + public async Task UseRecoveryCode(UseRecoveryCodeViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(model.Code); + if (result.Succeeded) + { + return RedirectToLocal(model.ReturnUrl); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid code."); + return View(model); + } + } + + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + private Task GetCurrentUserAsync() + { + return _userManager.GetUserAsync(HttpContext.User); + } + + private IActionResult RedirectToLocal(string returnUrl) + { + if (Url.IsLocalUrl(returnUrl)) + { + return Redirect(returnUrl); + } + else + { + return RedirectToAction(nameof(HomeController.Index), "Home"); + } + } + + #endregion + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Controllers/HomeController.cs b/src/Identity/samples/IdentitySample.Mvc/Controllers/HomeController.cs new file mode 100644 index 0000000000..b98b2869ec --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Controllers/HomeController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; + +namespace IdentitySample.Controllers +{ + public class HomeController : Controller + { + [HttpGet] + public IActionResult Index() + { + return View(); + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Controllers/ManageController.cs b/src/Identity/samples/IdentitySample.Mvc/Controllers/ManageController.cs new file mode 100644 index 0000000000..628345fbe5 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Controllers/ManageController.cs @@ -0,0 +1,379 @@ +using System.Linq; +using System.Threading.Tasks; +using IdentitySample.Models; +using IdentitySample.Models.ManageViewModels; +using IdentitySample.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace IdentitySamples.Controllers +{ + + [Authorize] + public class ManageController : Controller + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly ISmsSender _smsSender; + private readonly ILogger _logger; + + public ManageController( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender, + ISmsSender smsSender, + ILoggerFactory loggerFactory) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _smsSender = smsSender; + _logger = loggerFactory.CreateLogger(); + } + + // + // GET: /Manage/Index + [HttpGet] + public async Task Index(ManageMessageId? message = null) + { + ViewData["StatusMessage"] = + message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." + : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." + : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." + : message == ManageMessageId.Error ? "An error has occurred." + : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." + : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." + : ""; + + var user = await GetCurrentUserAsync(); + var model = new IndexViewModel + { + HasPassword = await _userManager.HasPasswordAsync(user), + PhoneNumber = await _userManager.GetPhoneNumberAsync(user), + TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user), + Logins = await _userManager.GetLoginsAsync(user), + BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user), + AuthenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user) + }; + return View(model); + } + + // + // POST: /Manage/RemoveLogin + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemoveLogin(RemoveLoginViewModel account) + { + ManageMessageId? message = ManageMessageId.Error; + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + message = ManageMessageId.RemoveLoginSuccess; + } + } + return RedirectToAction(nameof(ManageLogins), new { Message = message }); + } + + // + // GET: /Manage/AddPhoneNumber + public IActionResult AddPhoneNumber() + { + return View(); + } + + // + // POST: /Manage/AddPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AddPhoneNumber(AddPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + // Generate the token and send it + var user = await GetCurrentUserAsync(); + var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); + await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code); + return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber }); + } + + // + // POST: /Manage/ResetAuthenticatorKey + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ResetAuthenticatorKey() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + _logger.LogInformation(1, "User reset authenticator key."); + } + return RedirectToAction(nameof(Index), "Manage"); + } + + // + // POST: /Manage/GenerateRecoveryCode + [HttpPost] + [ValidateAntiForgeryToken] + public async Task GenerateRecoveryCode() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + var codes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 5); + _logger.LogInformation(1, "User generated new recovery code."); + return View("DisplayRecoveryCodes", new DisplayRecoveryCodesViewModel { Codes = codes }); + } + return View("Error"); + } + + // + // POST: /Manage/EnableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EnableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await _userManager.SetTwoFactorEnabledAsync(user, true); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(1, "User enabled two-factor authentication."); + } + return RedirectToAction(nameof(Index), "Manage"); + } + + // + // POST: /Manage/DisableTwoFactorAuthentication + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DisableTwoFactorAuthentication() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(2, "User disabled two-factor authentication."); + } + return RedirectToAction(nameof(Index), "Manage"); + } + + // + // GET: /Manage/VerifyPhoneNumber + [HttpGet] + public async Task VerifyPhoneNumber(string phoneNumber) + { + var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); + // Send an SMS to verify the phone number + return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); + } + + // + // POST: /Manage/VerifyPhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess }); + } + } + // If we got this far, something failed, redisplay the form + ModelState.AddModelError(string.Empty, "Failed to verify phone number"); + return View(model); + } + + // + // GET: /Manage/RemovePhoneNumber + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemovePhoneNumber() + { + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.SetPhoneNumberAsync(user, null); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); + } + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/ChangePassword + [HttpGet] + public IActionResult ChangePassword() + { + return View(); + } + + // + // POST: /Manage/ChangePassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation(3, "User changed their password successfully."); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + // + // GET: /Manage/SetPassword + [HttpGet] + public IActionResult SetPassword() + { + return View(); + } + + // + // POST: /Manage/SetPassword + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SetPassword(SetPasswordViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await GetCurrentUserAsync(); + if (user != null) + { + var result = await _userManager.AddPasswordAsync(user, model.NewPassword); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess }); + } + AddErrors(result); + return View(model); + } + return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); + } + + //GET: /Manage/ManageLogins + [HttpGet] + public async Task ManageLogins(ManageMessageId? message = null) + { + ViewData["StatusMessage"] = + message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." + : message == ManageMessageId.AddLoginSuccess ? "The external login was added." + : message == ManageMessageId.Error ? "An error has occurred." + : ""; + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var userLogins = await _userManager.GetLoginsAsync(user); + var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync(); + var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList(); + ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1; + return View(new ManageLoginsViewModel + { + CurrentLogins = userLogins, + OtherLogins = otherLogins + }); + } + + // + // POST: /Manage/LinkLogin + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult LinkLogin(string provider) + { + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + return Challenge(properties, provider); + } + + // + // GET: /Manage/LinkLoginCallback + [HttpGet] + public async Task LinkLoginCallback() + { + var user = await GetCurrentUserAsync(); + if (user == null) + { + return View("Error"); + } + var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); + if (info == null) + { + return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error }); + } + var result = await _userManager.AddLoginAsync(user, info); + var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; + return RedirectToAction(nameof(ManageLogins), new { Message = message }); + } + + #region Helpers + + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + public enum ManageMessageId + { + AddPhoneSuccess, + AddLoginSuccess, + ChangePasswordSuccess, + SetTwoFactorSuccess, + SetPasswordSuccess, + RemoveLoginSuccess, + RemovePhoneSuccess, + Error + } + + private Task GetCurrentUserAsync() + { + return _userManager.GetUserAsync(HttpContext.User); + } + + #endregion + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/CopyAspNetLoader.cmd b/src/Identity/samples/IdentitySample.Mvc/CopyAspNetLoader.cmd new file mode 100644 index 0000000000..f23e9109d0 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/CopyAspNetLoader.cmd @@ -0,0 +1,7 @@ +REM copy the AspNet.Loader.dll to bin folder +md bin + +REM figure out the path of AspNet.Loader.dll +FOR /F %%j IN ('dir /b /o:-d ..\..\packages\Microsoft.AspNetCore.Loader.IIS.Interop*') do (SET AspNetLoaderPath=..\..\packages\%%j\tools\AspNet.Loader.dll) +echo Found AspNetLoader.dll at %AspNetLoaderPath%. Copying to bin\ +copy %AspNetLoaderPath% bin\ \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..c68785939b --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IdentitySample.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace IdentitySample.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.0.0-rc3") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => + { + b.Property("Id"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("WebApplication13.Models.ApplicationUser", b => + { + b.Property("Id"); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasAnnotation("MaxLength", 256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedUserName") + .HasAnnotation("MaxLength", 256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.HasOne("WebApplication13.Models.ApplicationUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.HasOne("WebApplication13.Models.ApplicationUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("WebApplication13.Models.ApplicationUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.cs b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..7233e2d3bc --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace IdentitySample.Data.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(nullable: false), + ConcurrencyStamp = table.Column(nullable: true), + Name = table.Column(maxLength: 256, nullable: true), + NormalizedName = table.Column(maxLength: 256, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(nullable: false), + Name = table.Column(nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false), + ConcurrencyStamp = table.Column(nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false), + LockoutEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + NormalizedUserName = table.Column(maxLength: 256, nullable: true), + PasswordHash = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + SecurityStamp = table.Column(nullable: true), + TwoFactorEnabled = table.Column(nullable: false), + UserName = table.Column(maxLength: 256, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(nullable: false), + ProviderKey = table.Column(nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_UserId", + table: "AspNetUserRoles", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..ff4714e16a --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IdentitySample.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace IdentitySample.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.0.0-rc3") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => + { + b.Property("Id"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("IdentitySample.Models.ApplicationUser", b => + { + b.Property("Id"); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasAnnotation("MaxLength", 256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasAnnotation("MaxLength", 256); + + b.Property("NormalizedUserName") + .HasAnnotation("MaxLength", 256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasAnnotation("MaxLength", 256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim", b => + { + b.HasOne("IdentitySample.Models.ApplicationUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin", b => + { + b.HasOne("IdentitySample.Models.ApplicationUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") + .WithMany("Users") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("IdentitySample.Models.ApplicationUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/IdentitySample.Mvc.csproj b/src/Identity/samples/IdentitySample.Mvc/IdentitySample.Mvc.csproj new file mode 100644 index 0000000000..07074aac42 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/IdentitySample.Mvc.csproj @@ -0,0 +1,34 @@ + + + + Identity sample MVC application on ASP.NET Core + netcoreapp2.1;net461 + aspnetcore-b3d20cbe-418e-4bf2-a0f4-57f91d067e07 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Identity/samples/IdentitySample.Mvc/MessageServices.cs b/src/Identity/samples/IdentitySample.Mvc/MessageServices.cs new file mode 100644 index 0000000000..6860d99af9 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/MessageServices.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; + +namespace IdentitySamples +{ + public static class MessageServices + { + public static Task SendEmailAsync(string email, string subject, string message) + { + // Plug in your email service + return Task.FromResult(0); + } + + public static Task SendSmsAsync(string number, string message) + { + // Plug in your sms service + return Task.FromResult(0); + } + + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs new file mode 100644 index 0000000000..da18df3ef2 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.AccountViewModels +{ + public class ExternalLoginConfirmationViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ForgotPasswordViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ForgotPasswordViewModel.cs new file mode 100644 index 0000000000..7685e0ec19 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ForgotPasswordViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.AccountViewModels +{ + public class ForgotPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/LoginViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/LoginViewModel.cs new file mode 100644 index 0000000000..7b269fb517 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/LoginViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.AccountViewModels +{ + public class LoginViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/RegisterViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/RegisterViewModel.cs new file mode 100644 index 0000000000..99bf5fa910 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/RegisterViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.AccountViewModels +{ + public class RegisterViewModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ResetPasswordViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ResetPasswordViewModel.cs new file mode 100644 index 0000000000..89959c847f --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/ResetPasswordViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.AccountViewModels +{ + public class ResetPasswordViewModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/SendCodeViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/SendCodeViewModel.cs new file mode 100644 index 0000000000..7d87167457 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/SendCodeViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace IdentitySample.Models.AccountViewModels +{ + public class SendCodeViewModel + { + public string SelectedProvider { get; set; } + + public ICollection Providers { get; set; } + + public string ReturnUrl { get; set; } + + public bool RememberMe { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/UseRecoveryCodeViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/UseRecoveryCodeViewModel.cs new file mode 100644 index 0000000000..f3b6027557 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/UseRecoveryCodeViewModel.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace IdentitySample.Models.AccountViewModels +{ + public class UseRecoveryCodeViewModel + { + [Required] + public string Code { get; set; } + + public string ReturnUrl { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyAuthenticatorCodeViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyAuthenticatorCodeViewModel.cs new file mode 100644 index 0000000000..790d841f15 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyAuthenticatorCodeViewModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace IdentitySample.Models.AccountViewModels +{ + public class VerifyAuthenticatorCodeViewModel + { + [Required] + public string Code { get; set; } + + public string ReturnUrl { get; set; } + + [Display(Name = "Remember this browser?")] + public bool RememberBrowser { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyCodeViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyCodeViewModel.cs new file mode 100644 index 0000000000..caf3457f4a --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/AccountViewModels/VerifyCodeViewModel.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace IdentitySample.Models.AccountViewModels +{ + public class VerifyCodeViewModel + { + [Required] + public string Provider { get; set; } + + [Required] + public string Code { get; set; } + + public string ReturnUrl { get; set; } + + [Display(Name = "Remember this browser?")] + public bool RememberBrowser { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationDbContext.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationDbContext.cs new file mode 100644 index 0000000000..902414c46c --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationDbContext.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace IdentitySample.Models +{ + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + // Customize the ASP.NET Identity model and override the defaults if needed. + // For example, you can rename the ASP.NET Identity table names and more. + // Add your customizations after calling base.OnModelCreating(builder); + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationUser.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationUser.cs new file mode 100644 index 0000000000..1034780fdf --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ApplicationUser.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; + +namespace IdentitySample.Models +{ + // Add profile data for application users by adding properties to the ApplicationUser class + public class ApplicationUser : IdentityUser + { + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/AddPhoneNumberViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/AddPhoneNumberViewModel.cs new file mode 100644 index 0000000000..28f0d3aafb --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/AddPhoneNumberViewModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class AddPhoneNumberViewModel + { + [Required] + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ChangePasswordViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ChangePasswordViewModel.cs new file mode 100644 index 0000000000..c30745e582 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ChangePasswordViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class ChangePasswordViewModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs new file mode 100644 index 0000000000..76d371bfdf --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ConfigureTwoFactorViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace IdentitySample.Models.ManageViewModels +{ + public class ConfigureTwoFactorViewModel + { + public string SelectedProvider { get; set; } + + public ICollection Providers { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/DisplayRecoveryCodesViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/DisplayRecoveryCodesViewModel.cs new file mode 100644 index 0000000000..cd91aa2b79 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/DisplayRecoveryCodesViewModel.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IdentitySample.Models.ManageViewModels +{ + public class DisplayRecoveryCodesViewModel + { + [Required] + public IEnumerable Codes { get; set; } + + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/FactorViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/FactorViewModel.cs new file mode 100644 index 0000000000..88b968ae71 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/FactorViewModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class FactorViewModel + { + public string Purpose { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/IndexViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/IndexViewModel.cs new file mode 100644 index 0000000000..e8967b459e --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/IndexViewModel.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Identity; + +namespace IdentitySample.Models.ManageViewModels +{ + public class IndexViewModel + { + public bool HasPassword { get; set; } + + public IList Logins { get; set; } + + public string PhoneNumber { get; set; } + + public bool TwoFactor { get; set; } + + public bool BrowserRemembered { get; set; } + + public string AuthenticatorKey { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs new file mode 100644 index 0000000000..b7bd94a195 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; + +namespace IdentitySample.Models.ManageViewModels +{ + public class ManageLoginsViewModel + { + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/RemoveLoginViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/RemoveLoginViewModel.cs new file mode 100644 index 0000000000..b46474e669 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/RemoveLoginViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class RemoveLoginViewModel + { + public string LoginProvider { get; set; } + public string ProviderKey { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/SetPasswordViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/SetPasswordViewModel.cs new file mode 100644 index 0000000000..2e5fc4369c --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/SetPasswordViewModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class SetPasswordViewModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs new file mode 100644 index 0000000000..fbe22f4c86 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Models/ManageViewModels/VerifyPhoneNumberViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Models.ManageViewModels +{ + public class VerifyPhoneNumberViewModel + { + [Required] + public string Code { get; set; } + + [Required] + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Program.cs b/src/Identity/samples/IdentitySample.Mvc/Program.cs new file mode 100644 index 0000000000..03110954a3 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Program.cs @@ -0,0 +1,20 @@ +using System.IO; +using Microsoft.AspNetCore.Hosting; + +namespace IdentitySample +{ + public static class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Properties/launchSettings.json b/src/Identity/samples/IdentitySample.Mvc/Properties/launchSettings.json new file mode 100644 index 0000000000..503bada5ef --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51567/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "web": { + "commandName": "web", + "environmentVariables": { + "ASPNET_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Services/IEmailSender.cs b/src/Identity/samples/IdentitySample.Mvc/Services/IEmailSender.cs new file mode 100644 index 0000000000..f8a5ef6d59 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Services/IEmailSender.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Services +{ + public interface IEmailSender + { + Task SendEmailAsync(string email, string subject, string message); + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Services/ISmsSender.cs b/src/Identity/samples/IdentitySample.Mvc/Services/ISmsSender.cs new file mode 100644 index 0000000000..169b56bac7 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Services/ISmsSender.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Services +{ + public interface ISmsSender + { + Task SendSmsAsync(string number, string message); + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Services/MessageServices.cs b/src/Identity/samples/IdentitySample.Mvc/Services/MessageServices.cs new file mode 100644 index 0000000000..469bb05c77 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Services/MessageServices.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IdentitySample.Services +{ + // This class is used by the application to send Email and SMS + // when you turn on two-factor authentication in ASP.NET Identity. + // For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713 + public class AuthMessageSender : IEmailSender, ISmsSender + { + public Task SendEmailAsync(string email, string subject, string message) + { + // Plug in your email service here to send an email. + return Task.FromResult(0); + } + + public Task SendSmsAsync(string number, string message) + { + // Plug in your SMS service here to send a text message. + return Task.FromResult(0); + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Startup.cs b/src/Identity/samples/IdentitySample.Mvc/Startup.cs new file mode 100644 index 0000000000..6f6134a62e --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Startup.cs @@ -0,0 +1,80 @@ +using IdentitySample.Models; +using IdentitySample.Services; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace IdentitySample +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + + builder.AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; set; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add framework services. + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); + + services.AddMvc(); + + services.AddIdentityCore() + .AddRoles() + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + + services.AddAuthentication(o => + { + o.DefaultScheme = IdentityConstants.ApplicationScheme; + o.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(o => { }); + + // Add application services. + services.AddTransient(); + services.AddTransient(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseDatabaseErrorPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseAuthentication(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ConfirmEmail.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ConfirmEmail.cshtml new file mode 100644 index 0000000000..3244fef5f1 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ConfirmEmail.cshtml @@ -0,0 +1,10 @@ +@{ + ViewData["Title"] = "Confirm Email"; +} + +

@ViewData["Title"].

+
+

+ Thank you for confirming your email. Please Click here to Log in. +

+
diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginConfirmation.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginConfirmation.cshtml new file mode 100644 index 0000000000..fcd3b18691 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginConfirmation.cshtml @@ -0,0 +1,35 @@ +@model ExternalLoginConfirmationViewModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"].

+

Associate your @ViewData["LoginProvider"] account.

+ +
+

Association Form

+
+
+ +

+ You've successfully authenticated with @ViewData["LoginProvider"]. + Please enter a user name for this site below and click the Register button to finish + logging in. +

+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginFailure.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginFailure.cshtml new file mode 100644 index 0000000000..d89339e012 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ExternalLoginFailure.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Login Failure"; +} + +
+

@ViewData["Title"].

+

Unsuccessful login with service.

+
diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPassword.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPassword.cshtml new file mode 100644 index 0000000000..8400cc7e28 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPassword.cshtml @@ -0,0 +1,31 @@ +@model ForgotPasswordViewModel +@{ + ViewData["Title"] = "Forgot your password?"; +} + +

@ViewData["Title"].

+

+ For more information on how to enable reset password please see this article. +

+ +@*
+

Enter your email.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
*@ + +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPasswordConfirmation.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 0000000000..ab9bf44c8d --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Forgot Password Confirmation"; +} + +

@ViewData["Title"].

+

+ Please check your email to reset your password. +

diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/Lockout.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Lockout.cshtml new file mode 100644 index 0000000000..2cc946d5c4 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Lockout.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Locked out"; +} + +
+

Locked out.

+

This account has been locked out, please try again later.

+
diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/Login.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Login.cshtml new file mode 100644 index 0000000000..00e0e9ca33 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Login.cshtml @@ -0,0 +1,93 @@ +@using System.Collections.Generic +@using Microsoft.AspNetCore.Http +@using Microsoft.AspNetCore.Authentication +@model LoginViewModel +@inject SignInManager SignInManager + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"].

+
+
+
+
+

Use a local account to log in.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ Register as a new user? +

+

+ Forgot your password? +

+
+
+
+
+
+

Use another service to log in.

+
+ @{ + var schemes = await SignInManager.GetExternalAuthenticationSchemesAsync(); + var loginProviders = schemes.ToList(); + if (loginProviders.Count == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in loginProviders) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/Register.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Register.cshtml new file mode 100644 index 0000000000..7d064f1b68 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/Register.cshtml @@ -0,0 +1,42 @@ +@model RegisterViewModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"].

+ +
+

Create a new account.

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPassword.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPassword.cshtml new file mode 100644 index 0000000000..8ab27b68ea --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPassword.cshtml @@ -0,0 +1,43 @@ +@model ResetPasswordViewModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"].

+ +
+

Reset your password.

+
+
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPasswordConfirmation.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 0000000000..bef2e4570d --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,8 @@ +@{ + ViewData["Title"] = "Reset password confirmation"; +} + +

@ViewData["Title"].

+

+ Your password has been reset. Please Click here to log in. +

diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/SendCode.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/SendCode.cshtml new file mode 100644 index 0000000000..0c87629986 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/SendCode.cshtml @@ -0,0 +1,21 @@ +@model SendCodeViewModel +@{ + ViewData["Title"] = "Send Verification Code"; +} + +

@ViewData["Title"].

+ +
+ +
+
+ Select Two-Factor Authentication Provider: + + +
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/UseRecoveryCode.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/UseRecoveryCode.cshtml new file mode 100644 index 0000000000..0b16cba7b2 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/UseRecoveryCode.cshtml @@ -0,0 +1,28 @@ +@model UseRecoveryCodeViewModel +@{ + ViewData["Title"] = "Use recovery code"; +} + +

@ViewData["Title"].

+ +
+
+

@ViewData["Status"]

+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyAuthenticatorCode.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyAuthenticatorCode.cshtml new file mode 100644 index 0000000000..a122eae8fa --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyAuthenticatorCode.cshtml @@ -0,0 +1,40 @@ +@model VerifyAuthenticatorCodeViewModel +@{ + ViewData["Title"] = "Verify"; +} + +

@ViewData["Title"].

+ +
+
+ +

@ViewData["Status"]

+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+

+ Lost your authenticator? +

+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyCode.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyCode.cshtml new file mode 100644 index 0000000000..3689cbba48 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Account/VerifyCode.cshtml @@ -0,0 +1,38 @@ +@model VerifyCodeViewModel +@{ + ViewData["Title"] = "Verify"; +} + +

@ViewData["Title"].

+ +
+
+ + +

@ViewData["Status"]

+
+
+ +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Home/Index.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Home/Index.cshtml new file mode 100644 index 0000000000..59dc45d93a --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Home/Index.cshtml @@ -0,0 +1,233 @@ +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + ViewBag.Title = "Home Page"; +} + +
+

ASP.NET Identity

+

ASP.NET Identity is the membership system for ASP.NET apps. Following are the features of ASP.NET Identity in this sample application.

+

Learn more »

+
+ +
+
+
+
Initialize ASP.NET Identity
+
+ You can initialize ASP.NET Identity when the application starts. Since ASP.NET Identity is Entity Framework based in this sample, + you can create DatabaseInitializer which is configured to get called each time the app starts. + Please look in App_Start\IdentityConfig.cs + This code shows the following +
    +
  • When should the Initializer run and when should the database be created
  • +
  • Create Admin user
  • +
  • Create Admin role
  • +
  • Add Admin user to Admin role
  • +
+
+
+
+
+
+
Add profile data for the user
+
+ Please follow this tutorial. + +
    +
  • Add profile information in the Users Table
  • +
  • Look in Models\IdentityModels.cs for examples
  • +
+
+
+
+
+
+
Validation
+
+ When you create a User using a username or password, the Identity system performs validation on the username and password, and the passwords are hashed before they are + stored in the database. You can customize the validation by changing some of the properties of the validators such as Turn alphanumeric on/off, set minimum password length + or you can write your own custom validators and register them with the UserManager. +
+
+
+
+
+
Register a user and login
+
+ Click @Html.ActionLink("Register", "Register", "Account") and see the code in AccountController.cs and Register Action. + Click @Html.ActionLink("Log in", "Login", "Account") and see the code in AccountController.cs and Login Action. +
+
+
+
+
+
Social Logins
+
+ You can the support so that users can login using their Facebook, Google, Twitter, Microsoft Account and more. +
+
+ +
+
+
+
+
+
Basic User Management
+
+ Do Create, Update, List and Delete Users. + Assign a Role to a User. + Only Users In Role Admin can access this page. This uses the [Authorize(Roles = "Admin")] on the UserAdmin controller. +
+
+
+
+
+
Basic Role Management
+
+ Do Create, Update, List and Delete Roles. + Only Users In Role Admin can access this page. This authorization is done by using the [Authorize(Roles = "Admin")] on the RolesAdmin controller. +
+
+
+
+
+
Account Confirmation
+
+ When you register a new account, you will be sent an email confirmation. + You can use an email service such as SendGrid which integrates nicely with Windows Azure and requires no configuration or + set up an SMTP server to send email. + You can send email using the EmailService which is registered in App_Start\IdentityConfig.cs +
+
+
+
+
+
Two-Factor Authentication
+
+ This sample shows how you can use Two-Factor authentication. This sample has a SMS and email service registered where you can send SMS or email for sending the security code. + You can add more two-factor authentication factors such as QR codes and plug them into ASP.NET Identity. +
    +
  • + You can use a SMS using Twilio or use any means of sending SMS. Please read for more details on using Twilio. + You can send SMS using the SmsService which is registered in App_Start\IdentityConfig.cs +
  • +
  • + You can use an email service such as SendGrid or + set up an SMTP server to send email. + You can send email using the EmailService which is registered in App_Start\IdentityConfig.cs +
  • + +
  • + When you login, you can add a phone number by clicking the Manage page. +
  • +
  • + Once you add a phone number and have the Phone service hooked to send a SMS, you will get a code through SMS to confirm your phone number. +
  • +
  • + In the Manage page, you can turn on Two-Factor authentication. +
  • +
  • + When you logout and login, after you enter the username and password, you will get an option of how to get the security code to use for two-factor authentication. +
  • +
  • + You can copy the code from your SMS or email and enter in the form to login. +
  • +
  • + The sample also shows how to protect against Brute force attacks against two-factor codes. When you enter a code incorrectly for 5 times then you will be + lockedout for 5 min before you can enter a new code. These settings can be configured in App_Start\IdentityConfig.cs by setting DefaultAccountLockoutTimeSpan and MaxFailedAccessAttemptsBeforeLockout on the UserManager. +
  • +
  • + If the machine you are browsing this website is your own machine, you can choose to check the "Remember Me" option after you enter the code. + This option will remember you forever on this machine and will not ask you for the two-factor authentication, the next time when you login to the website. + You can change your "Remember Me" settings for two-factor authentication in the Manage page. +
  • +
+
+
+
+
+
+
Account Lockout
+
+ Provide a way to Lockout out the user if the user enters their password or two-factor codes incorrectly. + The number of invalid attempts and the timespan for the users are locked out can be configured. + A developer can optionally turn off Account Lockout for certain user accounts should they need to. +
+
    +
  • Account LockOut settings can be configured in the UserManager in IdentityConfig.cs
  • +
+
+
+
+
+
Security Token provider
+
+ Support a way to regenerate the Security Token for the user in cases when the User changes there password or any other security related information such as removing an associated login(such as Facebook, Google, Microsoft Account etc). + This is needed to ensure that any tokens generated with the old password are invalidated. In the sample project, if you change the users password then a new token is generated for the user and any previous tokens are invalidated. + This feature provides an extra layer of security to your application since when you change your password, you will be logged out from everywhere (all other browsers) where you have logged into this application. +
+
+
    +
  • The provider is registered when you add CookieAuthentication in StartupAuth to your application.
  • +
+
+
+
+
+
+
Password Reset
+
+ Allows the user to reset their passwords if they have forgotten their password. In this sample users need to confirm their email before they can reset their passwords. +
+
+
+
+
+
Custom Storage providers
+
+ You can extend ASP.NET Identity to write your own custom storage provider for storing the ASP.NET Identity system and user data + in a persistance system of your choice such as MondoDb, RavenDb, Azure Table Storage etc. +
+
+ +
+
+
+
+
+
Documentation
+
+ +
+
+
+
diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/AddPhoneNumber.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/AddPhoneNumber.cshtml new file mode 100644 index 0000000000..1e09409ca7 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/AddPhoneNumber.cshtml @@ -0,0 +1,27 @@ +@model AddPhoneNumberViewModel +@{ + ViewData["Title"] = "Add Phone Number"; +} + +

@ViewData["Title"].

+
+

Add a phone number.

+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ChangePassword.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ChangePassword.cshtml new file mode 100644 index 0000000000..6127082df1 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ChangePassword.cshtml @@ -0,0 +1,42 @@ +@model ChangePasswordViewModel +@{ + ViewData["Title"] = "Change Password"; +} + +

@ViewData["Title"].

+ +
+

Change Password Form

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/DisplayRecoveryCodes.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/DisplayRecoveryCodes.cshtml new file mode 100644 index 0000000000..00df7b2d60 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/DisplayRecoveryCodes.cshtml @@ -0,0 +1,21 @@ +@model DisplayRecoveryCodesViewModel +@{ + ViewData["Title"] = "Your recovery codes:"; +} + +

@ViewData["Title"].

+

@ViewData["StatusMessage"]

+ +
+

Here are your new recovery codes

+
+
+
Codes:
+ @foreach (var code in Model.Codes) + { +
+ @code +
+ } +
+
\ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/Index.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/Index.cshtml new file mode 100644 index 0000000000..6e6ac54fa7 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/Index.cshtml @@ -0,0 +1,86 @@ +@model IndexViewModel +@{ + ViewData["Title"] = "Manage your account"; +} + +

@ViewData["Title"].

+

@ViewData["StatusMessage"]

+ +
+

Change your account settings

+
+
+
Password:
+
+ @if (Model.HasPassword) + { + [  Change  ] + } + else + { + [  Create  ] + } +
+
External Logins:
+
+ @Model.Logins.Count [  Manage  ] +
+
Phone Number:
+
+

+ Phone Numbers can used as a second factor of verification in two-factor authentication. + See this article + for details on setting up this ASP.NET application to support two-factor authentication using SMS. +

+ @(Model.PhoneNumber ?? "None") + @if (Model.PhoneNumber != null) + { +
+ [  Change  ] +
+ [] +
+ } + else + { + [  Add  ] + } +
+ +
Two-Factor Authentication:
+
+ + @if (Model.TwoFactor) + { +
+ Enabled [] +
+ } + else + { +
+ [] Disabled +
+ } +
+
Authentication App:
+
+ @if (Model.AuthenticatorKey == null) + { +
+ Generate [] +
+ } + else + { + Your key is: @Model.AuthenticatorKey +
+ Generate [] +
+ } +
+
+
\ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml new file mode 100644 index 0000000000..5fe5ed475e --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml @@ -0,0 +1,54 @@ +@model ManageLoginsViewModel +@using Microsoft.AspNetCore.Authentication +@{ + ViewData["Title"] = "Manage your external logins"; +} + +

@ViewData["Title"].

+ +

@ViewData["StatusMessage"]

+@if (Model.CurrentLogins.Count > 0) +{ +

Registered Logins

+ + + @for (var index = 0; index < Model.CurrentLogins.Count; index++) + { + + + + + } + +
@Model.CurrentLogins[index].LoginProvider + @if ((bool)ViewData["ShowRemoveButton"]) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins.Count > 0) +{ +

Add another service to log in.

+
+
+
+

+ @foreach (var provider in Model.OtherLogins) + { + + } +

+
+
+} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/SetPassword.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/SetPassword.cshtml new file mode 100644 index 0000000000..d1a1b77a44 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/SetPassword.cshtml @@ -0,0 +1,38 @@ +@model SetPasswordViewModel +@{ + ViewData["Title"] = "Set Password"; +} + +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+ +
+

Set your password

+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Manage/VerifyPhoneNumber.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/VerifyPhoneNumber.cshtml new file mode 100644 index 0000000000..17787816be --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Manage/VerifyPhoneNumber.cshtml @@ -0,0 +1,30 @@ +@model VerifyPhoneNumberViewModel +@{ + ViewData["Title"] = "Verify Phone Number"; +} + +

@ViewData["Title"].

+ +
+ +

Add a phone number.

+
@ViewData["Status"]
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+ +@section Scripts { + @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_Layout.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..15c0be6c6a --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_Layout.cshtml @@ -0,0 +1,44 @@ + + + + + + @ViewData["Title"] - Identity Sample + + + + + +
+ @RenderBody() +
+
+

© 2016 - IdentitySample

+
+
+ + + + + + @RenderSection("scripts", required: false) + + diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_LoginPartial.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 0000000000..87d35e3aba --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,26 @@ +@using Microsoft.AspNetCore.Identity +@using IdentitySample.Models + +@inject SignInManager SignInManager +@inject UserManager UserManager + +@if (SignInManager.IsSignedIn(User)) +{ + +} +else +{ + +} \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_ValidationScriptsPartial.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000000..a6c30d4d82 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/Shared/_ValidationScriptsPartial.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/_ViewImports.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..fbba680c39 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/_ViewImports.cshtml @@ -0,0 +1,6 @@ +@using IdentitySample +@using IdentitySample.Models +@using IdentitySample.Models.AccountViewModels +@using IdentitySample.Models.ManageViewModels +@using Microsoft.AspNetCore.Identity +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Identity/samples/IdentitySample.Mvc/Views/_ViewStart.cshtml b/src/Identity/samples/IdentitySample.Mvc/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..a5f10045db --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/Identity/samples/IdentitySample.Mvc/appsettings.json b/src/Identity/samples/IdentitySample.Mvc/appsettings.json new file mode 100644 index 0000000000..fa18416de1 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/appsettings.json @@ -0,0 +1,13 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=IdentitySample-MVC-1-10;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/web.Debug.config b/src/Identity/samples/IdentitySample.Mvc/web.Debug.config new file mode 100644 index 0000000000..2e302f9f95 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/web.Release.config b/src/Identity/samples/IdentitySample.Mvc/web.Release.config new file mode 100644 index 0000000000..c35844462b --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/web.config b/src/Identity/samples/IdentitySample.Mvc/web.config new file mode 100644 index 0000000000..f7ac679334 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/web.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/IdentitySample.Mvc/wwwroot/css/site.css b/src/Identity/samples/IdentitySample.Mvc/wwwroot/css/site.css new file mode 100644 index 0000000000..1eaae3d043 --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/wwwroot/css/site.css @@ -0,0 +1,34 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Wrapping element */ +/* Set some basic padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Set widths on the form inputs since otherwise they're 100% wide */ +input, +select, +textarea { + max-width: 280px; +} + +/* Carousel */ +.carousel-caption { + z-index: 10 !important; +} + + .carousel-caption p { + font-size: 20px; + line-height: 1.4; + } + +@media (min-width: 768px) { + .carousel-caption { + z-index: 10 !important; + } +} diff --git a/src/Identity/samples/IdentitySample.Mvc/wwwroot/favicon.ico b/src/Identity/samples/IdentitySample.Mvc/wwwroot/favicon.ico new file mode 100644 index 0000000000..a3a799985c Binary files /dev/null and b/src/Identity/samples/IdentitySample.Mvc/wwwroot/favicon.ico differ diff --git a/src/Identity/samples/IdentitySample.Mvc/wwwroot/js/site.js b/src/Identity/samples/IdentitySample.Mvc/wwwroot/js/site.js new file mode 100644 index 0000000000..82ecce7b4a --- /dev/null +++ b/src/Identity/samples/IdentitySample.Mvc/wwwroot/js/site.js @@ -0,0 +1 @@ +// Write your Javascript code. diff --git a/src/Identity/samples/NativeWPFClient/App.config b/src/Identity/samples/NativeWPFClient/App.config new file mode 100644 index 0000000000..2ae8254d30 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Identity/samples/NativeWPFClient/App.xaml b/src/Identity/samples/NativeWPFClient/App.xaml new file mode 100644 index 0000000000..08a4b73ba3 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/src/Identity/samples/NativeWPFClient/App.xaml.cs b/src/Identity/samples/NativeWPFClient/App.xaml.cs new file mode 100644 index 0000000000..ece2e4d8e7 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace NativeWPFClient +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/src/Identity/samples/NativeWPFClient/MainWindow.xaml b/src/Identity/samples/NativeWPFClient/MainWindow.xaml new file mode 100644 index 0000000000..d943d68bd4 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/MainWindow.xaml @@ -0,0 +1,25 @@ + + + + diff --git a/src/Identity/samples/NativeWPFClient/MainWindow.xaml.cs b/src/Identity/samples/NativeWPFClient/MainWindow.xaml.cs new file mode 100644 index 0000000000..a6dcf6cdf5 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/MainWindow.xaml.cs @@ -0,0 +1,139 @@ +using Microsoft.Identity.Client; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace NativeWPFClient +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + private Task _currentAuthorization; + + public MainWindow() + { + InitializeComponent(); + // Local client + DataContext = new NativeWPFClientViewModel + { + BaseAddress = "https://localhost:44324/", + RedirectUri = "urn:ietf:wg:oauth:2.0:oob", + Tenant = "Identity", + Policy = "signinsignup", + ClientId = "06D7C2FB-A66A-41AD-9509-77BDDFAB111B", + Scopes = "https://localhost/DFC7191F-FF74-42B9-A292-08FEA80F5B20/v2.0/ProtectedApi/read" + }; + + // DataContext = new NativeWPFClientViewModel + // { + // BaseAddress = "https://login.microsoftonline.com/", + // RedirectUri = "urn:ietf:wg:oauth:2.0:oob", + // Tenant = "jacalvarb2c.onmicrosoft.com", + // Policy = "B2C_1_signinsignup", + // ClientId = "42291769-0dc8-4497-9cbc-d3879783d6e7", + // Scopes = "https://jacalvarb2c.onmicrosoft.com/ProtectedApi/read" + // }; + + ViewModel.Result = "Hit authorize to sign in"; + } + + NativeWPFClientViewModel ViewModel => (NativeWPFClientViewModel)DataContext; + + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + } + + private async void Authorize_Click(object sender, RoutedEventArgs e) + { + if (_currentAuthorization == null) + { + Authorize.IsEnabled = false; + await AuthorizeAsync(); + } + } + + private async Task AuthorizeAsync() + { + var authority = $"{ViewModel.BaseAddress}tfp/{ViewModel.Tenant}/{ViewModel.Policy}"; + var client = new PublicClientApplication(ViewModel.ClientId, authority) + { + ValidateAuthority = false + }; + try + { + var scope = new string[] { }; + var appScopes = ViewModel.Scopes.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + var currentAuthorization = await client.AcquireTokenAsync( + appScopes, + user: null, + behavior: UIBehavior.ForceLogin, + extraQueryParameters: string.Empty, + extraScopesToConsent: null, + authority: authority); + + ViewModel.Result = currentAuthorization.User?.Name ?? "Authenticated but no name"; + } + catch (MsalException ex) + { + if (ex.ErrorCode != "authentication_canceled") + { + // An unexpected error occurred. + string message = ex.Message; + if (ex.InnerException != null) + { + message += "Inner Exception : " + ex.InnerException.Message; + } + + MessageBox.Show(message); + } + } + finally + { + _currentAuthorization = null; + Authorize.IsEnabled = true; + } + } + } + + internal class NativeWPFClientViewModel : INotifyPropertyChanged + { + private string _result; + + public string BaseAddress { get; set; } + public string RedirectUri { get; set; } + public string Tenant { get; set; } + public string Policy { get; set; } + public string ClientId { get; set; } + public string Scopes { get; set; } + + + public string Result + { + get => _result; + set + { + _result = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + } +} diff --git a/src/Identity/samples/NativeWPFClient/NativeWPFClient.csproj b/src/Identity/samples/NativeWPFClient/NativeWPFClient.csproj new file mode 100644 index 0000000000..cfdc468083 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/NativeWPFClient.csproj @@ -0,0 +1,107 @@ + + + + + false + Debug + AnyCPU + {39AA4E4D-5E62-4213-8641-BF8012D45DE4} + WinExe + NativeWPFClient + NativeWPFClient + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + $(BaseNuGetRuntimeIdentifier) + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + Designer + + + + + 1.1.0-preview + + + + \ No newline at end of file diff --git a/src/Identity/samples/NativeWPFClient/Properties/AssemblyInfo.cs b/src/Identity/samples/NativeWPFClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d958efd3d3 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NativeWPFClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NativeWPFClient")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Identity/samples/NativeWPFClient/Properties/Resources.Designer.cs b/src/Identity/samples/NativeWPFClient/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..76b4a5f9d8 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NativeWPFClient.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NativeWPFClient.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/src/Identity/samples/NativeWPFClient/Properties/Resources.resx b/src/Identity/samples/NativeWPFClient/Properties/Resources.resx new file mode 100644 index 0000000000..9c90483af8 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Identity/samples/NativeWPFClient/Properties/Settings.Designer.cs b/src/Identity/samples/NativeWPFClient/Properties/Settings.Designer.cs new file mode 100644 index 0000000000..2f38c66821 --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NativeWPFClient.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/src/Identity/samples/NativeWPFClient/Properties/Settings.settings b/src/Identity/samples/NativeWPFClient/Properties/Settings.settings new file mode 100644 index 0000000000..033d7a5e9e --- /dev/null +++ b/src/Identity/samples/NativeWPFClient/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/Identity/src/AspNetCoreCompat/CookieInterop.cs b/src/Identity/src/AspNetCoreCompat/CookieInterop.cs new file mode 100644 index 0000000000..593c50ee77 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/CookieInterop.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Interop; + +namespace Owin +{ + public static class CookieInterop + { + public static ISecureDataFormat CreateSharedDataFormat(DirectoryInfo keyDirectory, string authenticationType) + { + var dataProtector = DataProtectionProvider.Create(keyDirectory) + .CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", // full name of the ASP.NET 5 type + authenticationType, "v2"); + return new AspNetTicketDataFormat(new DataProtectorShim(dataProtector)); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/AspNetCoreCompat/IdentityDbContext.cs b/src/Identity/src/AspNetCoreCompat/IdentityDbContext.cs new file mode 100644 index 0000000000..e773e39574 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/IdentityDbContext.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation, Inc. All rights reserved. +// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.Entity; +using System.Data.Entity.Infrastructure; +using System.Data.Entity.Validation; +using Microsoft.AspNet.Identity.EntityFramework; + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class IdentityDbContext : + IdentityDbContext + where TUser : IdentityUser + { + public IdentityDbContext() : base() + { + + } + + public IdentityDbContext(DbCompiledModel model) + : base(model) + { + + } + + public IdentityDbContext(string nameOrConnectionString) + : base(nameOrConnectionString) + { + + } + + public IdentityDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) + { + + } + + public IdentityDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) + { + + } + // + // Summary: + // Constructs a new context instance using the existing connection to connect to + // a database, and initializes it from the given model. The connection will not + // be disposed when the context is disposed if contextOwnsConnection is false. + // + // Parameters: + // existingConnection: + // An existing connection to use for the new context. + // + // model: + // The model that will back this context. + // + // contextOwnsConnection: + // Constructs a new context instance using the existing connection to connect to + // a database, and initializes it from the given model. The connection will not + // be disposed when the context is disposed if contextOwnsConnection is false. + public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { + } + } + + public class IdentityDbContext : + IdentityDbContext + where TUser : IdentityUser + where TRole : IdentityRole + where TUserLogin : IdentityUserLogin + where TUserRole : IdentityUserRole + where TUserClaim : IdentityUserClaim + where TRoleClaim : IdentityRoleClaim + { + + public IdentityDbContext() : base() + { + } + + public IdentityDbContext(DbCompiledModel model) + : base(model) + { + } + + public IdentityDbContext(string nameOrConnectionString) + : base(nameOrConnectionString) + { + } + + public IdentityDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) + { + } + + public IdentityDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) + { + } + + public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { + } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + var userModel = modelBuilder.Entity(); + + userModel.Property(x => x.LockoutEndDateUtc).HasColumnName("LockoutEnd"); + userModel.Property(x => x.ConcurrencyStamp).IsConcurrencyToken(true); + + modelBuilder.Entity() + .HasKey(x => x.Id) + .Map(config => config.ToTable("AspNetRoleClaims")); + + modelBuilder.Entity().Property(x => x.ConcurrencyStamp).IsConcurrencyToken(true); + } + + protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary items) + { + if (entityEntry.Entity is TUser && entityEntry.State == EntityState.Modified) + { + entityEntry.Cast().Property(p => p.ConcurrencyStamp).CurrentValue = Guid.NewGuid().ToString(); + } + else if (entityEntry.Entity is TRole && entityEntry.State == EntityState.Modified) + { + entityEntry.Cast().Property(p => p.ConcurrencyStamp).CurrentValue = Guid.NewGuid().ToString(); + } + return base.ValidateEntity(entityEntry, items); + } + + public virtual IDbSet RoleClaims { get; set; } + } +} diff --git a/src/Identity/src/AspNetCoreCompat/IdentityRole.cs b/src/Identity/src/AspNetCoreCompat/IdentityRole.cs new file mode 100644 index 0000000000..c671706d92 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/IdentityRole.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation, Inc. All rights reserved. +// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Identity.EntityFramework; + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class IdentityRole : IdentityRole + { + /// + /// Constructor + /// + public IdentityRole() + { + Id = Guid.NewGuid().ToString(); + } + + /// + /// Constructor + /// + /// + public IdentityRole(string roleName) + : this() + { + Name = roleName; + } + } + + public class IdentityRole : Microsoft.AspNet.Identity.EntityFramework.IdentityRole + where TUserRole : IdentityUserRole + { + /// + /// Normalized role name + /// + public virtual string NormalizedName { get; set; } + + /// + /// Concurrency stamp + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Navigation property for claims in the role + /// + public virtual ICollection> Claims { get; } = new List>(); + } +} + diff --git a/src/Identity/src/AspNetCoreCompat/IdentityRoleClaim.cs b/src/Identity/src/AspNetCoreCompat/IdentityRoleClaim.cs new file mode 100644 index 0000000000..c6b10687b4 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/IdentityRoleClaim.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation, Inc. All rights reserved. +// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class IdentityRoleClaim : IdentityRoleClaim { } + + /// + /// EntityType that represents one specific role claim + /// + /// + public class IdentityRoleClaim + { + /// + /// Primary key + /// + public virtual int Id { get; set; } + + /// + /// User Id for the role this claim belongs to + /// + public virtual TKey RoleId { get; set; } + + /// + /// Claim type + /// + public virtual string ClaimType { get; set; } + + /// + /// Claim value + /// + public virtual string ClaimValue { get; set; } + } +} + diff --git a/src/Identity/src/AspNetCoreCompat/IdentityUser.cs b/src/Identity/src/AspNetCoreCompat/IdentityUser.cs new file mode 100644 index 0000000000..e9df36348b --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/IdentityUser.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation, Inc. All rights reserved. +// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Identity.EntityFramework; + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class IdentityUser : IdentityUser + { + /// + /// Constructor which creates a new Guid for the Id + /// + public IdentityUser() + { + Id = Guid.NewGuid().ToString(); + } + + /// + /// Constructor that takes a userName + /// + /// + public IdentityUser(string userName) + : this() + { + UserName = userName; + NormalizedUserName = userName.ToUpperInvariant(); + } + } + + public class IdentityUser + : Microsoft.AspNet.Identity.EntityFramework.IdentityUser + where TUserLogin : IdentityUserLogin + where TUserRole : IdentityUserRole + where TUserClaim : IdentityUserClaim + { + public string NormalizedUserName { + get + { + return UserName.ToUpperInvariant(); + } + set { } + } + + /// + /// Normalized email + /// + public string NormalizedEmail { + get + { + return Email.ToUpperInvariant(); + } + set { } + } + + /// + /// Concurrency stamp + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + } +} + diff --git a/src/Identity/src/AspNetCoreCompat/IdentityUserLogin.cs b/src/Identity/src/AspNetCoreCompat/IdentityUserLogin.cs new file mode 100644 index 0000000000..5d36abb580 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/IdentityUserLogin.cs @@ -0,0 +1,19 @@ +// 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. + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class IdentityUserLogin : IdentityUserLogin { } + + /// + /// Represents a login and its associated provider for a user. + /// + /// The type of the primary key of the user associated with this login. + public class IdentityUserLogin : EntityFramework.IdentityUserLogin + { + /// + /// Gets or sets the friendly name used in a UI for this login. + /// + public virtual string ProviderDisplayName { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/AspNetCoreCompat/Microsoft.AspNet.Identity.AspNetCoreCompat.csproj b/src/Identity/src/AspNetCoreCompat/Microsoft.AspNet.Identity.AspNetCoreCompat.csproj new file mode 100644 index 0000000000..4910d07d2a --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/Microsoft.AspNet.Identity.AspNetCoreCompat.csproj @@ -0,0 +1,21 @@ + + + + A compatibility layer for sharing identity databases between Microsoft.AspNet.Identity.EntityFramework and Microsoft.AspNetCore.Identity.EntityFrameworkCore. + $(ExperimentalVersionPrefix) + $(ExperimentalVersionSuffix) + false + $(ExperimentalPackageVersion) + net461 + $(NoWarn);CS1591 + true + aspnetcore;identity;membership + + + + + + + + + diff --git a/src/Identity/src/AspNetCoreCompat/UserStore.cs b/src/Identity/src/AspNetCoreCompat/UserStore.cs new file mode 100644 index 0000000000..1ecee58283 --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/UserStore.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation, Inc. All rights reserved. +// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Data.Entity; +using Microsoft.AspNet.Identity.EntityFramework; + +namespace Microsoft.AspNet.Identity.CoreCompat +{ + public class UserStore : + UserStore, IUserStore + where TUser : IdentityUser + { + /// + /// Default constructor which uses a new instance of a default EntityDbContext. + /// + public UserStore() + : this(new IdentityDbContext()) + { + DisposeContext = true; + } + + /// + /// Constructor + /// + /// + public UserStore(DbContext context) + : base(context) + { + } + } + + public class UserStore + : EntityFramework.UserStore + where TKey : IEquatable + where TUser : IdentityUser + where TRole : IdentityRole + where TUserLogin : IdentityUserLogin, new() + where TUserRole : IdentityUserRole, new() + where TUserClaim : IdentityUserClaim, new() + { + /// + /// Constructor + /// + /// + public UserStore(DbContext context) : base(context) { } + + } +} + diff --git a/src/Identity/src/AspNetCoreCompat/baseline.netframework.json b/src/Identity/src/AspNetCoreCompat/baseline.netframework.json new file mode 100644 index 0000000000..caa12678eb --- /dev/null +++ b/src/Identity/src/AspNetCoreCompat/baseline.netframework.json @@ -0,0 +1,887 @@ +{ + "AssemblyIdentity": "Microsoft.AspNet.Identity.AspNetCoreCompat, Version=0.4.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.IdentityDbContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "nameOrConnectionString", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "nameOrConnectionString", + "Type": "System.String" + }, + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "existingConnection", + "Type": "System.Data.Common.DbConnection" + }, + { + "Name": "contextOwnsConnection", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "existingConnection", + "Type": "System.Data.Common.DbConnection" + }, + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + }, + { + "Name": "contextOwnsConnection", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "OnModelCreating", + "Parameters": [ + { + "Name": "modelBuilder", + "Type": "System.Data.Entity.DbModelBuilder" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateEntity", + "Parameters": [ + { + "Name": "entityEntry", + "Type": "System.Data.Entity.Infrastructure.DbEntityEntry" + }, + { + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "ReturnType": "System.Data.Entity.Validation.DbEntityValidationResult", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleClaims", + "Parameters": [], + "ReturnType": "System.Data.Entity.IDbSet", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleClaims", + "Parameters": [ + { + "Name": "value", + "Type": "System.Data.Entity.IDbSet" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "nameOrConnectionString", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "nameOrConnectionString", + "Type": "System.String" + }, + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "existingConnection", + "Type": "System.Data.Common.DbConnection" + }, + { + "Name": "contextOwnsConnection", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "existingConnection", + "Type": "System.Data.Common.DbConnection" + }, + { + "Name": "model", + "Type": "System.Data.Entity.Infrastructure.DbCompiledModel" + }, + { + "Name": "contextOwnsConnection", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 4, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 5, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 6, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityRoleClaim" + ] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityRole", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.IdentityRole", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityRole", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.EntityFramework.IdentityRole", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_NormalizedName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConcurrencyStamp", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConcurrencyStamp", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Claims", + "Parameters": [], + "ReturnType": "System.Collections.Generic.ICollection>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole" + ] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityRoleClaim", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.IdentityRoleClaim", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityRoleClaim", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimValue", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimValue", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityUser", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.IdentityUser", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityUser", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.EntityFramework.IdentityUser", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_NormalizedUserName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedUserName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NormalizedEmail", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConcurrencyStamp", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConcurrencyStamp", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim" + ] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProviderDisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderDisplayName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.CoreCompat.UserStore", + "ImplementedInterfaces": [ + "Microsoft.AspNet.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "System.Data.Entity.DbContext" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNet.Identity.CoreCompat.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNet.Identity.EntityFramework.UserStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "System.Data.Entity.DbContext" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.CoreCompat.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 5, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim" + ] + } + ] + }, + { + "Name": "Owin.CookieInterop", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateSharedDataFormat", + "Parameters": [ + { + "Name": "keyDirectory", + "Type": "System.IO.DirectoryInfo" + }, + { + "Name": "authenticationType", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.Owin.Security.ISecureDataFormat", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/Core/AuthenticatorTokenProvider.cs b/src/Identity/src/Core/AuthenticatorTokenProvider.cs new file mode 100644 index 0000000000..9f611e44fb --- /dev/null +++ b/src/Identity/src/Core/AuthenticatorTokenProvider.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used for authenticator code verification. + /// + public class AuthenticatorTokenProvider : IUserTwoFactorTokenProvider where TUser : class + { + /// + /// Checks if a two factor authentication token can be generated for the specified . + /// + /// The to retrieve the from. + /// The to check for the possibility of generating a two factor authentication token. + /// True if the user has an authenticator key set, otherwise false. + public async virtual Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + { + var key = await manager.GetAuthenticatorKeyAsync(user); + return !string.IsNullOrWhiteSpace(key); + } + + /// + /// Returns an empty string since no authenticator codes are sent. + /// + /// Ignored. + /// The to retrieve the from. + /// The . + /// string.Empty. + public virtual Task GenerateAsync(string purpose, UserManager manager, TUser user) + { + return Task.FromResult(string.Empty); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + { + var key = await manager.GetAuthenticatorKeyAsync(user); + int code; + if (!int.TryParse(token, out code)) + { + return false; + } + + var hash = new HMACSHA1(Base32.FromBase32(key)); + var unixTimestamp = Convert.ToInt64(Math.Round((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds)); + var timestep = Convert.ToInt64(unixTimestamp / 30); + // Allow codes from 90s in each direction (we could make this configurable?) + for (int i = -2; i <= 2; i++) + { + var expectedCode = Rfc6238AuthenticationService.ComputeTotp(hash, (ulong)(timestep + i), modifier: null); + if (expectedCode == code) + { + return true; + } + } + return false; + } + } +} diff --git a/src/Identity/src/Core/Base32.cs b/src/Identity/src/Core/Base32.cs new file mode 100644 index 0000000000..579a58feac --- /dev/null +++ b/src/Identity/src/Core/Base32.cs @@ -0,0 +1,119 @@ +// 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.Text; + +namespace Microsoft.AspNetCore.Identity +{ + // See http://tools.ietf.org/html/rfc3548#section-5 + internal static class Base32 + { + private static readonly string _base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + public static string ToBase32(byte[] input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + StringBuilder sb = new StringBuilder(); + for (int offset = 0; offset < input.Length;) + { + byte a, b, c, d, e, f, g, h; + int numCharsToOutput = GetNextGroup(input, ref offset, out a, out b, out c, out d, out e, out f, out g, out h); + + sb.Append((numCharsToOutput >= 1) ? _base32Chars[a] : '='); + sb.Append((numCharsToOutput >= 2) ? _base32Chars[b] : '='); + sb.Append((numCharsToOutput >= 3) ? _base32Chars[c] : '='); + sb.Append((numCharsToOutput >= 4) ? _base32Chars[d] : '='); + sb.Append((numCharsToOutput >= 5) ? _base32Chars[e] : '='); + sb.Append((numCharsToOutput >= 6) ? _base32Chars[f] : '='); + sb.Append((numCharsToOutput >= 7) ? _base32Chars[g] : '='); + sb.Append((numCharsToOutput >= 8) ? _base32Chars[h] : '='); + } + + return sb.ToString(); + } + + public static byte[] FromBase32(string input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + input = input.TrimEnd('=').ToUpperInvariant(); + if (input.Length == 0) + { + return new byte[0]; + } + + var output = new byte[input.Length * 5 / 8]; + var bitIndex = 0; + var inputIndex = 0; + var outputBits = 0; + var outputIndex = 0; + while (outputIndex < output.Length) + { + var byteIndex = _base32Chars.IndexOf(input[inputIndex]); + if (byteIndex < 0) + { + throw new FormatException(); + } + + var bits = Math.Min(5 - bitIndex, 8 - outputBits); + output[outputIndex] <<= bits; + output[outputIndex] |= (byte)(byteIndex >> (5 - (bitIndex + bits))); + + bitIndex += bits; + if (bitIndex >= 5) + { + inputIndex++; + bitIndex = 0; + } + + outputBits += bits; + if (outputBits >= 8) + { + outputIndex++; + outputBits = 0; + } + } + return output; + } + + // returns the number of bytes that were output + private static int GetNextGroup(byte[] input, ref int offset, out byte a, out byte b, out byte c, out byte d, out byte e, out byte f, out byte g, out byte h) + { + uint b1, b2, b3, b4, b5; + + int retVal; + switch (offset - input.Length) + { + case 1: retVal = 2; break; + case 2: retVal = 4; break; + case 3: retVal = 5; break; + case 4: retVal = 7; break; + default: retVal = 8; break; + } + + b1 = (offset < input.Length) ? input[offset++] : 0U; + b2 = (offset < input.Length) ? input[offset++] : 0U; + b3 = (offset < input.Length) ? input[offset++] : 0U; + b4 = (offset < input.Length) ? input[offset++] : 0U; + b5 = (offset < input.Length) ? input[offset++] : 0U; + + a = (byte)(b1 >> 3); + b = (byte)(((b1 & 0x07) << 2) | (b2 >> 6)); + c = (byte)((b2 >> 1) & 0x1f); + d = (byte)(((b2 & 0x01) << 4) | (b3 >> 4)); + e = (byte)(((b3 & 0x0f) << 1) | (b4 >> 7)); + f = (byte)((b4 >> 2) & 0x1f); + g = (byte)(((b4 & 0x3) << 3) | (b5 >> 5)); + h = (byte)(b5 & 0x1f); + + return retVal; + } + } +} diff --git a/src/Identity/src/Core/ClaimsIdentityOptions.cs b/src/Identity/src/Core/ClaimsIdentityOptions.cs new file mode 100644 index 0000000000..54ba1ae069 --- /dev/null +++ b/src/Identity/src/Core/ClaimsIdentityOptions.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options used to configure the claim types used for well known claims. + /// + public class ClaimsIdentityOptions + { + /// + /// Gets or sets the ClaimType used for a Role claim. Defaults to . + /// + public string RoleClaimType { get; set; } = ClaimTypes.Role; + + /// + /// Gets or sets the ClaimType used for the user name claim. Defaults to . + /// + public string UserNameClaimType { get; set; } = ClaimTypes.Name; + + /// + /// Gets or sets the ClaimType used for the user identifier claim. Defaults to . + /// + public string UserIdClaimType { get; set; } = ClaimTypes.NameIdentifier; + + /// + /// Gets or sets the ClaimType used for the security stamp claim. Defaults to "AspNet.Identity.SecurityStamp". + /// + public string SecurityStampClaimType { get; set; } = "AspNet.Identity.SecurityStamp"; + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/DefaultPersonalDataProtector.cs b/src/Identity/src/Core/DefaultPersonalDataProtector.cs new file mode 100644 index 0000000000..6fef248043 --- /dev/null +++ b/src/Identity/src/Core/DefaultPersonalDataProtector.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Default implementation of that uses + /// and to protect data with a payload format of {keyId}:{protectedData} + /// + public class DefaultPersonalDataProtector : IPersonalDataProtector + { + private readonly ILookupProtectorKeyRing _keyRing; + private readonly ILookupProtector _encryptor; + + /// + /// Constructor. + /// + /// + /// + public DefaultPersonalDataProtector(ILookupProtectorKeyRing keyRing, ILookupProtector protector) + { + _keyRing = keyRing; + _encryptor = protector; + } + + /// + /// Unprotect the data. + /// + /// The data to unprotect. + /// The unprotected data. + public virtual string Unprotect(string data) + { + var split = data.IndexOf(':'); + if (split == -1 || split == data.Length-1) + { + throw new InvalidOperationException("Malformed data."); + } + + var keyId = data.Substring(0, split); + return _encryptor.Unprotect(keyId, data.Substring(split + 1)); + } + + /// + /// Protect the data. + /// + /// The data to protect. + /// The protected data. + public virtual string Protect(string data) + { + var current = _keyRing.CurrentKeyId; + return current + ":" + _encryptor.Protect(current, data); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/EmailTokenProvider.cs b/src/Identity/src/Core/EmailTokenProvider.cs new file mode 100644 index 0000000000..a6ee043876 --- /dev/null +++ b/src/Identity/src/Core/EmailTokenProvider.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// TokenProvider that generates tokens from the user's security stamp and notifies a user via email. + /// + /// The type used to represent a user. + public class EmailTokenProvider : TotpSecurityStampBasedTokenProvider + where TUser : class + { + /// + /// Checks if a two factor authentication token can be generated for the specified . + /// + /// The to retrieve the from. + /// The to check for the possibility of generating a two factor authentication token. + /// True if the user has an email address set, otherwise false. + public override async Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + { + var email = await manager.GetEmailAsync(user); + return !string.IsNullOrWhiteSpace(email) && await manager.IsEmailConfirmedAsync(user); + } + + /// + /// Returns the a value for the user used as entropy in the generated token. + /// + /// The purpose of the two factor authentication token. + /// The to retrieve the from. + /// The to check for the possibility of generating a two factor authentication token. + /// A string suitable for use as entropy in token generation. + public override async Task GetUserModifierAsync(string purpose, UserManager manager, + TUser user) + { + var email = await manager.GetEmailAsync(user); + return "Email:" + purpose + ":" + email; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/ILookupNormalizer.cs b/src/Identity/src/Core/ILookupNormalizer.cs new file mode 100644 index 0000000000..7cefc4a8a0 --- /dev/null +++ b/src/Identity/src/Core/ILookupNormalizer.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for normalizing keys for lookup purposes. + /// + public interface ILookupNormalizer + { + /// + /// Returns a normalized representation of the specified . + /// + /// The key to normalize. + /// A normalized representation of the specified . + string Normalize(string key); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/ILookupProtector.cs b/src/Identity/src/Core/ILookupProtector.cs new file mode 100644 index 0000000000..21b1feec41 --- /dev/null +++ b/src/Identity/src/Core/ILookupProtector.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to protect/unprotect lookups with a specific key. + /// + public interface ILookupProtector + { + /// + /// Protect the data using the specified key. + /// + /// The key to use. + /// The data to protect. + /// The protected data. + string Protect(string keyId, string data); + + /// + /// Unprotect the data using the specified key. + /// + /// The key to use. + /// The data to unprotect. + /// The original data. + string Unprotect(string keyId, string data); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/ILookupProtectorKeyRing.cs b/src/Identity/src/Core/ILookupProtectorKeyRing.cs new file mode 100644 index 0000000000..a25f7c7ef5 --- /dev/null +++ b/src/Identity/src/Core/ILookupProtectorKeyRing.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Abstraction used to manage named keys used to protect lookups. + /// + public interface ILookupProtectorKeyRing + { + /// + /// Get the current key id. + /// + string CurrentKeyId { get; } + + /// + /// Return a specific key. + /// + /// The id of the key to fetch. + /// The key ring. + string this[string keyId] { get; } + + /// + /// Return all of the key ids. + /// + /// All of the key ids. + IEnumerable GetAllKeyIds(); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IPasswordHasher.cs b/src/Identity/src/Core/IPasswordHasher.cs new file mode 100644 index 0000000000..d7dae3411d --- /dev/null +++ b/src/Identity/src/Core/IPasswordHasher.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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for hashing passwords. + /// + /// The type used to represent a user. + public interface IPasswordHasher where TUser : class + { + /// + /// Returns a hashed representation of the supplied for the specified . + /// + /// The user whose password is to be hashed. + /// The password to hash. + /// A hashed representation of the supplied for the specified . + string HashPassword(TUser user, string password); + + /// + /// Returns a indicating the result of a password hash comparison. + /// + /// The user whose password should be verified. + /// The hash value for a user's stored password. + /// The password supplied for comparison. + /// A indicating the result of a password hash comparison. + /// Implementations of this method should be time consistent. + PasswordVerificationResult VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IPasswordValidator.cs b/src/Identity/src/Core/IPasswordValidator.cs new file mode 100644 index 0000000000..a60285a285 --- /dev/null +++ b/src/Identity/src/Core/IPasswordValidator.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for validating passwords. + /// + /// The type that represents a user. + public interface IPasswordValidator where TUser : class + { + /// + /// Validates a password as an asynchronous operation. + /// + /// The to retrieve the properties from. + /// The user whose password should be validated. + /// The password supplied for validation + /// The task object representing the asynchronous operation. + Task ValidateAsync(UserManager manager, TUser user, string password); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IPersonalDataProtector.cs b/src/Identity/src/Core/IPersonalDataProtector.cs new file mode 100644 index 0000000000..6c543aecfa --- /dev/null +++ b/src/Identity/src/Core/IPersonalDataProtector.cs @@ -0,0 +1,28 @@ +// 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; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction used for personal data encryption. + /// + public interface IPersonalDataProtector + { + /// + /// Protect the data. + /// + /// The data to protect. + /// The protected data. + string Protect(string data); + + /// + /// Unprotect the data. + /// + /// + /// The unprotected data. + string Unprotect(string data); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IProtectedUserStore.cs b/src/Identity/src/Core/IProtectedUserStore.cs new file mode 100644 index 0000000000..332f0a5916 --- /dev/null +++ b/src/Identity/src/Core/IProtectedUserStore.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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Marker interface used to signal that the store supports the flag. + /// + /// The type that represents a user. + public interface IProtectedUserStore : IUserStore where TUser : class + { } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IQueryableRoleStore.cs b/src/Identity/src/Core/IQueryableRoleStore.cs new file mode 100644 index 0000000000..053d6bd3b0 --- /dev/null +++ b/src/Identity/src/Core/IQueryableRoleStore.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for querying roles in a Role store. + /// + /// The type encapsulating a role. + public interface IQueryableRoleStore : IRoleStore where TRole : class + { + /// + /// Returns an collection of roles. + /// + /// An collection of roles. + IQueryable Roles { get; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IQueryableUserStore.cs b/src/Identity/src/Core/IQueryableUserStore.cs new file mode 100644 index 0000000000..68b39b8fd2 --- /dev/null +++ b/src/Identity/src/Core/IQueryableUserStore.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for querying users in a User store. + /// + /// The type encapsulating a user. + public interface IQueryableUserStore : IUserStore where TUser : class + { + /// + /// Returns an collection of users. + /// + /// An collection of users. + IQueryable Users { get; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IRoleClaimStore.cs b/src/Identity/src/Core/IRoleClaimStore.cs new file mode 100644 index 0000000000..a37e5c720e --- /dev/null +++ b/src/Identity/src/Core/IRoleClaimStore.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 System.Collections.Generic; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store of role specific claims. + /// + /// The type encapsulating a role. + public interface IRoleClaimStore : IRoleStore where TRole : class + { + /// + /// Gets a list of s to be belonging to the specified as an asynchronous operation. + /// + /// The role whose claims to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a list of s. + /// + Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Add a new claim to a role as an asynchronous operation. + /// + /// The role to add a claim to. + /// The to add. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Remove a claim from a role as an asynchronous operation. + /// + /// The role to remove the claim from. + /// The to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IRoleStore.cs b/src/Identity/src/Core/IRoleStore.cs new file mode 100644 index 0000000000..e7d65ae08d --- /dev/null +++ b/src/Identity/src/Core/IRoleStore.cs @@ -0,0 +1,99 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a storage and management of roles. + /// + /// The type that represents a role. + public interface IRoleStore : IDisposable where TRole : class + { + /// + /// Creates a new role in a store as an asynchronous operation. + /// + /// The role to create in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + Task CreateAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Updates a role in a store as an asynchronous operation. + /// + /// The role to update in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + Task UpdateAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Deletes a role from the store as an asynchronous operation. + /// + /// The role to delete from the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + Task DeleteAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Gets the ID for a role from the store as an asynchronous operation. + /// + /// The role whose ID should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the ID of the role. + Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Gets the name of a role from the store as an asynchronous operation. + /// + /// The role whose name should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Sets the name of a role in the store as an asynchronous operation. + /// + /// The role whose name should be set. + /// The name of the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken); + + /// + /// Get a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken); + + /// + /// Set a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be set. + /// The normalized name to set + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken); + + + /// + /// Finds the role who has the specified ID as an asynchronous operation. + /// + /// The role ID to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + Task FindByIdAsync(string roleId, CancellationToken cancellationToken); + + /// + /// Finds the role who has the specified normalized name as an asynchronous operation. + /// + /// The normalized role name to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + Task FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IRoleValidator.cs b/src/Identity/src/Core/IRoleValidator.cs new file mode 100644 index 0000000000..f2a3a563e8 --- /dev/null +++ b/src/Identity/src/Core/IRoleValidator.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a validating a role. + /// + /// The type encapsulating a role. + public interface IRoleValidator where TRole : class + { + /// + /// Validates a role as an asynchronous operation. + /// + /// The managing the role store. + /// The role to validate. + /// A that represents the of the asynchronous validation. + Task ValidateAsync(RoleManager manager, TRole role); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserAuthenticationTokenStore.cs b/src/Identity/src/Core/IUserAuthenticationTokenStore.cs new file mode 100644 index 0000000000..9023bf6037 --- /dev/null +++ b/src/Identity/src/Core/IUserAuthenticationTokenStore.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction to store a user's authentication tokens. + /// + /// The type encapsulating a user. + public interface IUserAuthenticationTokenStore : IUserStore where TUser : class + { + /// + /// Sets the token value for a particular user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The value of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken); + + /// + /// Deletes a token for a user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken); + + /// + /// Returns the token value. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Core/IUserAuthenticatorKeyStore.cs b/src/Identity/src/Core/IUserAuthenticatorKeyStore.cs new file mode 100644 index 0000000000..d54039db90 --- /dev/null +++ b/src/Identity/src/Core/IUserAuthenticatorKeyStore.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store which stores info about user's authenticator. + /// + /// The type encapsulating a user. + public interface IUserAuthenticatorKeyStore : IUserStore where TUser : class + { + /// + /// Sets the authenticator key for the specified . + /// + /// The user whose authenticator key should be set. + /// The authenticator key to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken); + + /// + /// Get the authenticator key for the specified . + /// + /// The user whose security stamp should be set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + Task GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Core/IUserClaimStore.cs b/src/Identity/src/Core/IUserClaimStore.cs new file mode 100644 index 0000000000..90a4e7fee6 --- /dev/null +++ b/src/Identity/src/Core/IUserClaimStore.cs @@ -0,0 +1,66 @@ +// 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 System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store of claims for a user. + /// + /// The type encapsulating a user. + public interface IUserClaimStore : IUserStore where TUser : class + { + /// + /// Gets a list of s to be belonging to the specified as an asynchronous operation. + /// + /// The role whose claims to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a list of s. + /// + Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Add claims to a user as an asynchronous operation. + /// + /// The user to add the claim to. + /// The collection of s to add. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken); + + /// + /// Replaces the given on the specified with the + /// + /// The user to replace the claim on. + /// The claim to replace. + /// The new claim to replace the existing with. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken); + + /// + /// Removes the specified from the given . + /// + /// The user to remove the specified from. + /// A collection of s to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken); + + /// + /// Returns a list of users who contain the specified . + /// + /// The claim to look for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a list of who + /// contain the specified claim. + /// + Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserClaimsPrincipalFactory.cs b/src/Identity/src/Core/IUserClaimsPrincipalFactory.cs new file mode 100644 index 0000000000..20a766ff24 --- /dev/null +++ b/src/Identity/src/Core/IUserClaimsPrincipalFactory.cs @@ -0,0 +1,23 @@ +// 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.Security.Claims; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a factory to create a from a user. + /// + /// The type encapsulating a user. + public interface IUserClaimsPrincipalFactory + where TUser : class + { + /// + /// Creates a from an user asynchronously. + /// + /// The user to create a from. + /// The that represents the asynchronous creation operation, containing the created . + Task CreateAsync(TUser user); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserEmailStore.cs b/src/Identity/src/Core/IUserEmailStore.cs new file mode 100644 index 0000000000..4bdc662da7 --- /dev/null +++ b/src/Identity/src/Core/IUserEmailStore.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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for the storage and management of user email addresses. + /// + /// The type encapsulating a user. + public interface IUserEmailStore : IUserStore where TUser : class + { + /// + /// Sets the address for a . + /// + /// The user whose email should be set. + /// The email to set. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken); + + /// + /// Gets the email address for the specified . + /// + /// The user whose email should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// The task object containing the results of the asynchronous operation, the email address for the specified . + Task GetEmailAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Gets a flag indicating whether the email address for the specified has been verified, true if the email address is verified otherwise + /// false. + /// + /// The user whose email confirmation status should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified + /// has been confirmed or not. + /// + Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Sets the flag indicating whether the specified 's email address has been confirmed or not. + /// + /// The user whose email confirmation status should be set. + /// A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken); + + /// + /// Gets the user, if any, associated with the specified, normalized email address. + /// + /// The normalized email address to return the user for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address. + /// + Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken); + + /// + /// Returns the normalized email for the specified . + /// + /// The user whose email address to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user. + /// + Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Sets the normalized email for the specified . + /// + /// The user whose email address to set. + /// The normalized email to set for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserLockoutStore.cs b/src/Identity/src/Core/IUserLockoutStore.cs new file mode 100644 index 0000000000..c48df74642 --- /dev/null +++ b/src/Identity/src/Core/IUserLockoutStore.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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a storing information which can be used to implement account lockout, + /// including access failures and lockout status + /// + /// The type that represents a user. + public interface IUserLockoutStore : IUserStore where TUser : class + { + /// + /// Gets the last a user's last lockout expired, if any. + /// Any time in the past should be indicates a user is not locked out. + /// + /// The user whose lockout date should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a containing the last time + /// a user's lockout expired, if any. + /// + Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user. + /// + /// The user whose lockout date should be set. + /// The after which the 's lockout should end. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken); + + /// + /// Records that a failed access has occurred, incrementing the failed access count. + /// + /// The user whose cancellation count should be incremented. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the incremented failed access count. + Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Resets a user's failed access count. + /// + /// The user whose failed access count should be reset. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + /// This is typically called after the account is successfully accessed. + Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Retrieves the current failed access count for the specified . + /// + /// The user whose failed access count should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the failed access count. + Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Retrieves a flag indicating whether user lockout can enabled for the specified user. + /// + /// The user whose ability to be locked out should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, true if a user can be locked out, otherwise false. + /// + Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Set the flag indicating if the specified can be locked out. + /// + /// The user whose ability to be locked out should be set. + /// A flag indicating if lock out can be enabled for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserLoginStore.cs b/src/Identity/src/Core/IUserLoginStore.cs new file mode 100644 index 0000000000..519ba09274 --- /dev/null +++ b/src/Identity/src/Core/IUserLoginStore.cs @@ -0,0 +1,58 @@ +// 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 System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for storing information that maps external login information provided + /// by Microsoft Account, Facebook etc. to a user account. + /// + /// The type that represents a user. + public interface IUserLoginStore : IUserStore where TUser : class + { + /// + /// Adds an external to the specified . + /// + /// The user to add the login to. + /// The external to add to the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken); + + /// + /// Attempts to remove the provided login information from the specified . + /// and returns a flag indicating whether the removal succeed or not. + /// + /// The user to remove the login information from. + /// The login provide whose information should be removed. + /// The key given by the external login provider for the specified user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken); + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Retrieves the user associated with the specified login provider and login provider key. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserPasswordStore.cs b/src/Identity/src/Core/IUserPasswordStore.cs new file mode 100644 index 0000000000..639451a121 --- /dev/null +++ b/src/Identity/src/Core/IUserPasswordStore.cs @@ -0,0 +1,43 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store containing users' password hashes. + /// + /// The type encapsulating a user. + public interface IUserPasswordStore : IUserStore where TUser : class + { + /// + /// Sets the password hash for the specified . + /// + /// The user whose password hash to set. + /// The password hash to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken); + + /// + /// Gets the password hash for the specified . + /// + /// The user whose password hash to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, returning the password hash for the specified . + Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Gets a flag indicating whether the specified has a password. + /// + /// The user to return a flag for, indicating whether they have a password or not. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, returning true if the specified has a password + /// otherwise false. + /// + Task HasPasswordAsync(TUser user, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserPhoneNumberStore.cs b/src/Identity/src/Core/IUserPhoneNumberStore.cs new file mode 100644 index 0000000000..e82d37549b --- /dev/null +++ b/src/Identity/src/Core/IUserPhoneNumberStore.cs @@ -0,0 +1,52 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store containing users' telephone numbers. + /// + /// The type encapsulating a user. + public interface IUserPhoneNumberStore : IUserStore where TUser : class + { + /// + /// Sets the telephone number for the specified . + /// + /// The user whose telephone number should be set. + /// The telephone number to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken); + + /// + /// Gets the telephone number, if any, for the specified . + /// + /// The user whose telephone number should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the user's telephone number, if any. + Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Gets a flag indicating whether the specified 's telephone number has been confirmed. + /// + /// The user to return a flag for, indicating whether their telephone number is confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, returning true if the specified has a confirmed + /// telephone number otherwise false. + /// + Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Sets a flag indicating if the specified 's phone number has been confirmed. + /// + /// The user whose telephone number confirmation status should be set. + /// A flag indicating whether the user's telephone number has been confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserRoleStore.cs b/src/Identity/src/Core/IUserRoleStore.cs new file mode 100644 index 0000000000..e4ef13bcb7 --- /dev/null +++ b/src/Identity/src/Core/IUserRoleStore.cs @@ -0,0 +1,64 @@ +// 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 System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store which maps users to roles. + /// + /// The type encapsulating a user. + public interface IUserRoleStore : IUserStore where TUser : class + { + /// + /// Add the specified to the named role. + /// + /// The user to add to the named role. + /// The name of the role to add the user to. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken); + + /// + /// Remove the specified from the named role. + /// + /// The user to remove the named role from. + /// The name of the role to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken); + + /// + /// Gets a list of role names the specified belongs to. + /// + /// The user whose role names to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing a list of role names. + Task> GetRolesAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Returns a flag indicating whether the specified is a member of the given named role. + /// + /// The user whose role membership should be checked. + /// The name of the role to be checked. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing a flag indicating whether the specified is + /// a member of the named role. + /// + Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken); + + /// + /// Returns a list of Users who are members of the named role. + /// + /// The name of the role whose membership should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing a list of users who are in the named role. + /// + Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Core/IUserSecurityStampStore.cs b/src/Identity/src/Core/IUserSecurityStampStore.cs new file mode 100644 index 0000000000..c09b16a815 --- /dev/null +++ b/src/Identity/src/Core/IUserSecurityStampStore.cs @@ -0,0 +1,32 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store which stores a user's security stamp. + /// + /// The type encapsulating a user. + public interface IUserSecurityStampStore : IUserStore where TUser : class + { + /// + /// Sets the provided security for the specified . + /// + /// The user whose security stamp should be set. + /// The security stamp to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken); + + /// + /// Get the security stamp for the specified . + /// + /// The user whose security stamp should be set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserStore.cs b/src/Identity/src/Core/IUserStore.cs new file mode 100644 index 0000000000..bbf1af3dcb --- /dev/null +++ b/src/Identity/src/Core/IUserStore.cs @@ -0,0 +1,102 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store which manages user accounts. + /// + /// The type encapsulating a user. + public interface IUserStore : IDisposable where TUser : class + { + /// + /// Gets the user identifier for the specified . + /// + /// The user whose identifier should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the identifier for the specified . + Task GetUserIdAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Gets the user name for the specified . + /// + /// The user whose name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the name for the specified . + Task GetUserNameAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Sets the given for the specified . + /// + /// The user whose name should be set. + /// The user name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken); + + /// + /// Gets the normalized user name for the specified . + /// + /// The user whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the normalized user name for the specified . + Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Sets the given normalized name for the specified . + /// + /// The user whose name should be set. + /// The normalized name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken); + + /// + /// Creates the specified in the user store. + /// + /// The user to create. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the creation operation. + Task CreateAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Updates the specified in the user store. + /// + /// The user to update. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + Task UpdateAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Deletes the specified from the user store. + /// + /// The user to delete. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + Task DeleteAsync(TUser user, CancellationToken cancellationToken); + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + Task FindByIdAsync(string userId, CancellationToken cancellationToken); + + /// + /// Finds and returns a user, if any, who has the specified normalized user name. + /// + /// The normalized user name to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Core/IUserTwoFactorRecoveryCodeStore.cs b/src/Identity/src/Core/IUserTwoFactorRecoveryCodeStore.cs new file mode 100644 index 0000000000..9de8e2e5ce --- /dev/null +++ b/src/Identity/src/Core/IUserTwoFactorRecoveryCodeStore.cs @@ -0,0 +1,43 @@ +// 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 System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a store which stores a user's recovery codes. + /// + /// The type encapsulating a user. + public interface IUserTwoFactorRecoveryCodeStore : IUserStore where TUser : class + { + /// + /// Updates the recovery codes for the user while invalidating any previous recovery codes. + /// + /// The user to store new recovery codes for. + /// The new recovery codes for the user. + /// The used to propagate notifications that the operation should be canceled. + /// The new recovery codes for the user. + Task ReplaceCodesAsync(TUser user, IEnumerable recoveryCodes, CancellationToken cancellationToken); + + /// + /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid + /// once, and will be invalid after use. + /// + /// The user who owns the recovery code. + /// The recovery code to use. + /// The used to propagate notifications that the operation should be canceled. + /// True if the recovery code was found for the user. + Task RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken); + + /// + /// Returns how many recovery code are still valid for a user. + /// + /// The user who owns the recovery code. + /// The used to propagate notifications that the operation should be canceled. + /// The number of valid recovery codes for the user.. + Task CountCodesAsync(TUser user, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IUserTwoFactorStore.cs b/src/Identity/src/Core/IUserTwoFactorStore.cs new file mode 100644 index 0000000000..614eb5cc5a --- /dev/null +++ b/src/Identity/src/Core/IUserTwoFactorStore.cs @@ -0,0 +1,37 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction to store a flag indicating whether a user has two factor authentication enabled. + /// + /// The type encapsulating a user. + public interface IUserTwoFactorStore : IUserStore where TUser : class + { + /// + /// Sets a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// A flag indicating whether the specified has two factor authentication enabled. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken); + + /// + /// Returns a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing a flag indicating whether the specified + /// has two factor authentication enabled or not. + /// + Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Core/IUserTwoFactorTokenProvider.cs b/src/Identity/src/Core/IUserTwoFactorTokenProvider.cs new file mode 100644 index 0000000000..5c9f7d82d6 --- /dev/null +++ b/src/Identity/src/Core/IUserTwoFactorTokenProvider.cs @@ -0,0 +1,63 @@ +// 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.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for two factor token generators. + /// + /// The type encapsulating a user. + public interface IUserTwoFactorTokenProvider where TUser : class + { + /// + /// Generates a token for the specified and . + /// + /// The purpose the token will be used for. + /// The that can be used to retrieve user properties. + /// The user a token should be generated for. + /// + /// The that represents the asynchronous operation, containing the token for the specified + /// and . + /// + /// + /// The parameter allows a token generator to be used for multiple types of token whilst + /// insuring a token for one purpose cannot be used for another. For example if you specified a purpose of "Email" + /// and validated it with the same purpose a token with the purpose of TOTP would not pass the check even if it was + /// for the same user. + /// + /// Implementations of should validate that purpose is not null or empty to + /// help with token separation. + /// + Task GenerateAsync(string purpose, UserManager manager, TUser user); + + /// + /// Returns a flag indicating whether the specified is valid for the given + /// and . + /// + /// The purpose the token will be used for. + /// The token to validate. + /// The that can be used to retrieve user properties. + /// The user a token should be validated for. + /// + /// The that represents the asynchronous operation, containing the a flag indicating the result + /// of validating the for the specified and . + /// The task will return true if the token is valid, otherwise false. + /// + Task ValidateAsync(string purpose, string token, UserManager manager, TUser user); + + /// + /// Returns a flag indicating whether the token provider can generate a token suitable for two factor authentication token for + /// the specified . + /// + /// The that can be used to retrieve user properties. + /// The user a token could be generated for. + /// + /// The that represents the asynchronous operation, containing the a flag indicating if a two + /// factor token could be generated by this provider for the specified . + /// The task will return true if a two factor authentication token could be generated, otherwise false. + /// + Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user); + } +} diff --git a/src/Identity/src/Core/IUserValidator.cs b/src/Identity/src/Core/IUserValidator.cs new file mode 100644 index 0000000000..763eddb229 --- /dev/null +++ b/src/Identity/src/Core/IUserValidator.cs @@ -0,0 +1,23 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for user validation. + /// + /// The type encapsulating a user. + public interface IUserValidator where TUser : class + { + /// + /// Validates the specified as an asynchronous operation. + /// + /// The that can be used to retrieve user properties. + /// The user to validate. + /// The that represents the asynchronous operation, containing the of the validation operation. + Task ValidateAsync(UserManager manager, TUser user); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IdentityBuilder.cs b/src/Identity/src/Core/IdentityBuilder.cs new file mode 100644 index 0000000000..2c75df148e --- /dev/null +++ b/src/Identity/src/Core/IdentityBuilder.cs @@ -0,0 +1,241 @@ +// 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.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Identity.Core; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Helper functions for configuring identity services. + /// + public class IdentityBuilder + { + /// + /// Creates a new instance of . + /// + /// The to use for the users. + /// The to attach to. + public IdentityBuilder(Type user, IServiceCollection services) + { + UserType = user; + Services = services; + } + + /// + /// Creates a new instance of . + /// + /// The to use for the users. + /// The to use for the roles. + /// The to attach to. + public IdentityBuilder(Type user, Type role, IServiceCollection services) : this(user, services) + => RoleType = role; + + /// + /// Gets the used for users. + /// + /// + /// The used for users. + /// + public Type UserType { get; private set; } + + + /// + /// Gets the used for roles. + /// + /// + /// The used for roles. + /// + public Type RoleType { get; private set; } + + /// + /// Gets the services are attached to. + /// + /// + /// The services are attached to. + /// + public IServiceCollection Services { get; private set; } + + private IdentityBuilder AddScoped(Type serviceType, Type concreteType) + { + Services.AddScoped(serviceType, concreteType); + return this; + } + + /// + /// Adds an for the . + /// + /// The user validator type. + /// The current instance. + public virtual IdentityBuilder AddUserValidator() where TValidator : class + => AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator)); + + /// + /// Adds an for the . + /// + /// The type of the claims principal factory. + /// The current instance. + public virtual IdentityBuilder AddClaimsPrincipalFactory() where TFactory : class + => AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory)); + + /// + /// Adds an . + /// + /// The type of the error describer. + /// The current instance. + public virtual IdentityBuilder AddErrorDescriber() where TDescriber : IdentityErrorDescriber + { + Services.AddScoped(); + return this; + } + + /// + /// Adds an for the . + /// + /// The validator type used to validate passwords. + /// The current instance. + public virtual IdentityBuilder AddPasswordValidator() where TValidator : class + => AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator)); + + /// + /// Adds an for the . + /// + /// The user store type. + /// The current instance. + public virtual IdentityBuilder AddUserStore() where TStore : class + => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore)); + + /// + /// Adds a token provider. + /// + /// The type of the token provider to add. + /// The name of the provider to add. + /// The current instance. + public virtual IdentityBuilder AddTokenProvider(string providerName) where TProvider : class + => AddTokenProvider(providerName, typeof(TProvider)); + + /// + /// Adds a token provider for the . + /// + /// The name of the provider to add. + /// The type of the to add. + /// The current instance. + public virtual IdentityBuilder AddTokenProvider(string providerName, Type provider) + { + if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).GetTypeInfo().IsAssignableFrom(provider.GetTypeInfo())) + { + throw new InvalidOperationException(Resources.FormatInvalidManagerType(provider.Name, "IUserTokenProvider", UserType.Name)); + } + Services.Configure(options => + { + options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider); + }); + Services.AddTransient(provider); + return this; + } + + /// + /// Adds a for the . + /// + /// The type of the user manager to add. + /// The current instance. + public virtual IdentityBuilder AddUserManager() where TUserManager : class + { + var userManagerType = typeof(UserManager<>).MakeGenericType(UserType); + var customType = typeof(TUserManager); + if (!userManagerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo())) + { + throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "UserManager", UserType.Name)); + } + if (userManagerType != customType) + { + Services.AddScoped(customType, services => services.GetRequiredService(userManagerType)); + } + return AddScoped(userManagerType, customType); + } + + /// + /// Adds Role related services for TRole, including IRoleStore, IRoleValidator, and RoleManager. + /// + /// The role type. + /// The current instance. + public virtual IdentityBuilder AddRoles() where TRole : class + { + RoleType = typeof(TRole); + AddRoleValidator>(); + Services.TryAddScoped, RoleManager>(); + return this; + } + + /// + /// Adds an for the . + /// + /// The role validator type. + /// The current instance. + public virtual IdentityBuilder AddRoleValidator() where TRole : class + { + if (RoleType == null) + { + throw new InvalidOperationException(Resources.NoRoleType); + } + return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(TRole)); + } + + /// + /// Adds an and . + /// + /// The personal data protector type. + /// The personal data protector key ring type. + /// The current instance. + public virtual IdentityBuilder AddPersonalDataProtection() + where TProtector : class,ILookupProtector + where TKeyRing : class, ILookupProtectorKeyRing + { + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + return this; + } + + /// + /// Adds a for the . + /// + /// The role store. + /// The current instance. + public virtual IdentityBuilder AddRoleStore() where TStore : class + { + if (RoleType == null) + { + throw new InvalidOperationException(Resources.NoRoleType); + } + return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(TStore)); + } + + /// + /// Adds a for the . + /// + /// The type of the role manager to add. + /// The current instance. + public virtual IdentityBuilder AddRoleManager() where TRoleManager : class + { + if (RoleType == null) + { + throw new InvalidOperationException(Resources.NoRoleType); + } + var managerType = typeof(RoleManager<>).MakeGenericType(RoleType); + var customType = typeof(TRoleManager); + if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo())) + { + throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "RoleManager", RoleType.Name)); + } + if (managerType != customType) + { + Services.AddScoped(typeof(TRoleManager), services => services.GetRequiredService(managerType)); + } + return AddScoped(managerType, typeof(TRoleManager)); + } + } +} diff --git a/src/Identity/src/Core/IdentityError.cs b/src/Identity/src/Core/IdentityError.cs new file mode 100644 index 0000000000..3f09da51e5 --- /dev/null +++ b/src/Identity/src/Core/IdentityError.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Encapsulates an error from the identity subsystem. + /// + public class IdentityError + { + /// + /// Gets or sets the code for this error. + /// + /// + /// The code for this error. + /// + public string Code { get; set; } + + /// + /// Gets or sets the description for this error. + /// + /// + /// The description for this error. + /// + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IdentityErrorDescriber.cs b/src/Identity/src/Core/IdentityErrorDescriber.cs new file mode 100644 index 0000000000..fdedb4ed0a --- /dev/null +++ b/src/Identity/src/Core/IdentityErrorDescriber.cs @@ -0,0 +1,312 @@ +// 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.Identity.Core; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Service to enable localization for application facing identity errors. + /// + /// + /// These errors are returned to controllers and are generally used as display messages to end users. + /// + public class IdentityErrorDescriber + { + /// + /// Returns the default . + /// + /// The default . + public virtual IdentityError DefaultError() + { + return new IdentityError + { + Code = nameof(DefaultError), + Description = Resources.DefaultError + }; + } + + /// + /// Returns an indicating a concurrency failure. + /// + /// An indicating a concurrency failure. + public virtual IdentityError ConcurrencyFailure() + { + return new IdentityError + { + Code = nameof(ConcurrencyFailure), + Description = Resources.ConcurrencyFailure + }; + } + + /// + /// Returns an indicating a password mismatch. + /// + /// An indicating a password mismatch. + public virtual IdentityError PasswordMismatch() + { + return new IdentityError + { + Code = nameof(PasswordMismatch), + Description = Resources.PasswordMismatch + }; + } + + /// + /// Returns an indicating an invalid token. + /// + /// An indicating an invalid token. + public virtual IdentityError InvalidToken() + { + return new IdentityError + { + Code = nameof(InvalidToken), + Description = Resources.InvalidToken + }; + } + + /// + /// Returns an indicating a recovery code was not redeemed. + /// + /// An indicating a recovery code was not redeemed. + public virtual IdentityError RecoveryCodeRedemptionFailed() + { + return new IdentityError + { + Code = nameof(RecoveryCodeRedemptionFailed), + Description = Resources.RecoveryCodeRedemptionFailed + }; + } + + /// + /// Returns an indicating an external login is already associated with an account. + /// + /// An indicating an external login is already associated with an account. + public virtual IdentityError LoginAlreadyAssociated() + { + return new IdentityError + { + Code = nameof(LoginAlreadyAssociated), + Description = Resources.LoginAlreadyAssociated + }; + } + + /// + /// Returns an indicating the specified user is invalid. + /// + /// The user name that is invalid. + /// An indicating the specified user is invalid. + public virtual IdentityError InvalidUserName(string userName) + { + return new IdentityError + { + Code = nameof(InvalidUserName), + Description = Resources.FormatInvalidUserName(userName) + }; + } + + /// + /// Returns an indicating the specified is invalid. + /// + /// The email that is invalid. + /// An indicating the specified is invalid. + public virtual IdentityError InvalidEmail(string email) + { + return new IdentityError + { + Code = nameof(InvalidEmail), + Description = Resources.FormatInvalidEmail(email) + }; + } + + /// + /// Returns an indicating the specified already exists. + /// + /// The user name that already exists. + /// An indicating the specified already exists. + public virtual IdentityError DuplicateUserName(string userName) + { + return new IdentityError + { + Code = nameof(DuplicateUserName), + Description = Resources.FormatDuplicateUserName(userName) + }; + } + + /// + /// Returns an indicating the specified is already associated with an account. + /// + /// The email that is already associated with an account. + /// An indicating the specified is already associated with an account. + public virtual IdentityError DuplicateEmail(string email) + { + return new IdentityError + { + Code = nameof(DuplicateEmail), + Description = Resources.FormatDuplicateEmail(email) + }; + } + + /// + /// Returns an indicating the specified name is invalid. + /// + /// The invalid role. + /// An indicating the specific role name is invalid. + public virtual IdentityError InvalidRoleName(string role) + { + return new IdentityError + { + Code = nameof(InvalidRoleName), + Description = Resources.FormatInvalidRoleName(role) + }; + } + + /// + /// Returns an indicating the specified name already exists. + /// + /// The duplicate role. + /// An indicating the specific role name already exists. + public virtual IdentityError DuplicateRoleName(string role) + { + return new IdentityError + { + Code = nameof(DuplicateRoleName), + Description = Resources.FormatDuplicateRoleName(role) + }; + } + + /// + /// Returns an indicating a user already has a password. + /// + /// An indicating a user already has a password. + public virtual IdentityError UserAlreadyHasPassword() + { + return new IdentityError + { + Code = nameof(UserAlreadyHasPassword), + Description = Resources.UserAlreadyHasPassword + }; + } + + /// + /// Returns an indicating user lockout is not enabled. + /// + /// An indicating user lockout is not enabled. + public virtual IdentityError UserLockoutNotEnabled() + { + return new IdentityError + { + Code = nameof(UserLockoutNotEnabled), + Description = Resources.UserLockoutNotEnabled + }; + } + + /// + /// Returns an indicating a user is already in the specified . + /// + /// The duplicate role. + /// An indicating a user is already in the specified . + public virtual IdentityError UserAlreadyInRole(string role) + { + return new IdentityError + { + Code = nameof(UserAlreadyInRole), + Description = Resources.FormatUserAlreadyInRole(role) + }; + } + + /// + /// Returns an indicating a user is not in the specified . + /// + /// The duplicate role. + /// An indicating a user is not in the specified . + public virtual IdentityError UserNotInRole(string role) + { + return new IdentityError + { + Code = nameof(UserNotInRole), + Description = Resources.FormatUserNotInRole(role) + }; + } + + /// + /// Returns an indicating a password of the specified does not meet the minimum length requirements. + /// + /// The length that is not long enough. + /// An indicating a password of the specified does not meet the minimum length requirements. + public virtual IdentityError PasswordTooShort(int length) + { + return new IdentityError + { + Code = nameof(PasswordTooShort), + Description = Resources.FormatPasswordTooShort(length) + }; + } + + /// + /// Returns an indicating a password does not meet the minimum number of unique chars. + /// + /// The number of different chars that must be used. + /// An indicating a password does not meet the minimum number of unique chars. + public virtual IdentityError PasswordRequiresUniqueChars(int uniqueChars) + { + return new IdentityError + { + Code = nameof(PasswordRequiresUniqueChars), + Description = Resources.FormatPasswordRequiresUniqueChars(uniqueChars) + }; + } + + /// + /// Returns an indicating a password entered does not contain a non-alphanumeric character, which is required by the password policy. + /// + /// An indicating a password entered does not contain a non-alphanumeric character. + public virtual IdentityError PasswordRequiresNonAlphanumeric() + { + return new IdentityError + { + Code = nameof(PasswordRequiresNonAlphanumeric), + Description = Resources.PasswordRequiresNonAlphanumeric + }; + } + + /// + /// Returns an indicating a password entered does not contain a numeric character, which is required by the password policy. + /// + /// An indicating a password entered does not contain a numeric character. + public virtual IdentityError PasswordRequiresDigit() + { + return new IdentityError + { + Code = nameof(PasswordRequiresDigit), + Description = Resources.PasswordRequiresDigit + }; + } + + /// + /// Returns an indicating a password entered does not contain a lower case letter, which is required by the password policy. + /// + /// An indicating a password entered does not contain a lower case letter. + public virtual IdentityError PasswordRequiresLower() + { + return new IdentityError + { + Code = nameof(PasswordRequiresLower), + Description = Resources.PasswordRequiresLower + }; + } + + /// + /// Returns an indicating a password entered does not contain an upper case letter, which is required by the password policy. + /// + /// An indicating a password entered does not contain an upper case letter. + public virtual IdentityError PasswordRequiresUpper() + { + return new IdentityError + { + Code = nameof(PasswordRequiresUpper), + Description = Resources.PasswordRequiresUpper + }; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IdentityOptions.cs b/src/Identity/src/Core/IdentityOptions.cs new file mode 100644 index 0000000000..c246ef9b61 --- /dev/null +++ b/src/Identity/src/Core/IdentityOptions.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents all the options you can use to configure the identity system. + /// + public class IdentityOptions + { + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public ClaimsIdentityOptions ClaimsIdentity { get; set; } = new ClaimsIdentityOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public UserOptions User { get; set; } = new UserOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public PasswordOptions Password { get; set; } = new PasswordOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public LockoutOptions Lockout { get; set; } = new LockoutOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public SignInOptions SignIn { get; set; } = new SignInOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public TokenOptions Tokens { get; set; } = new TokenOptions(); + + /// + /// Gets or sets the for the identity system. + /// + /// + /// The for the identity system. + /// + public StoreOptions Stores { get; set; } = new StoreOptions(); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IdentityResult.cs b/src/Identity/src/Core/IdentityResult.cs new file mode 100644 index 0000000000..8a525fb54a --- /dev/null +++ b/src/Identity/src/Core/IdentityResult.cs @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents the result of an identity operation. + /// + public class IdentityResult + { + private static readonly IdentityResult _success = new IdentityResult { Succeeded = true }; + private List _errors = new List(); + + /// + /// Flag indicating whether if the operation succeeded or not. + /// + /// True if the operation succeeded, otherwise false. + public bool Succeeded { get; protected set; } + + /// + /// An of s containing an errors + /// that occurred during the identity operation. + /// + /// An of s. + public IEnumerable Errors => _errors; + + /// + /// Returns an indicating a successful identity operation. + /// + /// An indicating a successful operation. + public static IdentityResult Success => _success; + + /// + /// Creates an indicating a failed identity operation, with a list of if applicable. + /// + /// An optional array of s which caused the operation to fail. + /// An indicating a failed identity operation, with a list of if applicable. + public static IdentityResult Failed(params IdentityError[] errors) + { + var result = new IdentityResult { Succeeded = false }; + if (errors != null) + { + result._errors.AddRange(errors); + } + return result; + } + + /// + /// Converts the value of the current object to its equivalent string representation. + /// + /// A string representation of the current object. + /// + /// If the operation was successful the ToString() will return "Succeeded" otherwise it returned + /// "Failed : " followed by a comma delimited list of error codes from its collection, if any. + /// + public override string ToString() + { + return Succeeded ? + "Succeeded" : + string.Format("{0} : {1}", "Failed", string.Join(",", Errors.Select(x => x.Code).ToList())); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/IdentityServiceCollectionExtensions.cs b/src/Identity/src/Core/IdentityServiceCollectionExtensions.cs new file mode 100644 index 0000000000..b4730bcf59 --- /dev/null +++ b/src/Identity/src/Core/IdentityServiceCollectionExtensions.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Contains extension methods to for configuring identity services. + /// + public static class IdentityServiceCollectionExtensions + { + /// + /// Adds and configures the identity system for the specified User and Role types. + /// + /// The type representing a User in the system. + /// The services available in the application. + /// An for creating and configuring the identity system. + public static IdentityBuilder AddIdentityCore(this IServiceCollection services) where TUser : class + => services.AddIdentityCore(o => { }); + + /// + /// Adds and configures the identity system for the specified User and Role types. + /// + /// The type representing a User in the system. + /// The services available in the application. + /// An action to configure the . + /// An for creating and configuring the identity system. + public static IdentityBuilder AddIdentityCore(this IServiceCollection services, Action setupAction) + where TUser : class + { + // Services identity depends on + services.AddOptions().AddLogging(); + + // Services used by identity + services.TryAddScoped, UserValidator>(); + services.TryAddScoped, PasswordValidator>(); + services.TryAddScoped, PasswordHasher>(); + services.TryAddScoped(); + // No interface for the error describer so we can add errors without rev'ing the interface + services.TryAddScoped(); + services.TryAddScoped, UserClaimsPrincipalFactory>(); + services.TryAddScoped, UserManager>(); + + if (setupAction != null) + { + services.Configure(setupAction); + } + + return new IdentityBuilder(typeof(TUser), services); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/LockoutOptions.cs b/src/Identity/src/Core/LockoutOptions.cs new file mode 100644 index 0000000000..6c28eb5adc --- /dev/null +++ b/src/Identity/src/Core/LockoutOptions.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; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options for configuring user lockout. + /// + public class LockoutOptions + { + /// + /// Gets or sets a flag indicating whether a new user can be locked out. Defaults to true. + /// + /// + /// True if a newly created user can be locked out, otherwise false. + /// + public bool AllowedForNewUsers { get; set; } = true; + + /// + /// Gets or sets the number of failed access attempts allowed before a user is locked out, + /// assuming lock out is enabled. Defaults to 5. + /// + /// + /// The number of failed access attempts allowed before a user is locked out, if lockout is enabled. + /// + public int MaxFailedAccessAttempts { get; set; } = 5; + + /// + /// Gets or sets the a user is locked out for when a lockout occurs. Defaults to 5 minutes. + /// + /// The a user is locked out for when a lockout occurs. + public TimeSpan DefaultLockoutTimeSpan { get; set; } = TimeSpan.FromMinutes(5); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/Microsoft.Extensions.Identity.Core.csproj b/src/Identity/src/Core/Microsoft.Extensions.Identity.Core.csproj new file mode 100644 index 0000000000..c446217071 --- /dev/null +++ b/src/Identity/src/Core/Microsoft.Extensions.Identity.Core.csproj @@ -0,0 +1,17 @@ + + + + ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. ASP.NET Core Identity allows you to add login features to your application and makes it easy to customize data about the logged in user. + netstandard2.0 + true + aspnetcore;identity;membership + + + + + + + + + + diff --git a/src/Identity/src/Core/PasswordHasher.cs b/src/Identity/src/Core/PasswordHasher.cs new file mode 100644 index 0000000000..ebc0ded3dc --- /dev/null +++ b/src/Identity/src/Core/PasswordHasher.cs @@ -0,0 +1,297 @@ +// 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.Runtime.CompilerServices; +using System.Security.Cryptography; +using Microsoft.AspNetCore.Cryptography.KeyDerivation; +using Microsoft.Extensions.Identity.Core; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Implements the standard Identity password hashing. + /// + /// The type used to represent a user. + public class PasswordHasher : IPasswordHasher where TUser : class + { + /* ======================= + * HASHED PASSWORD FORMATS + * ======================= + * + * Version 2: + * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. + * (See also: SDL crypto guidelines v5.1, Part III) + * Format: { 0x00, salt, subkey } + * + * Version 3: + * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations. + * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } + * (All UInt32s are stored big-endian.) + */ + + private readonly PasswordHasherCompatibilityMode _compatibilityMode; + private readonly int _iterCount; + private readonly RandomNumberGenerator _rng; + + /// + /// Creates a new instance of . + /// + /// The options for this instance. + public PasswordHasher(IOptions optionsAccessor = null) + { + var options = optionsAccessor?.Value ?? new PasswordHasherOptions(); + + _compatibilityMode = options.CompatibilityMode; + switch (_compatibilityMode) + { + case PasswordHasherCompatibilityMode.IdentityV2: + // nothing else to do + break; + + case PasswordHasherCompatibilityMode.IdentityV3: + _iterCount = options.IterationCount; + if (_iterCount < 1) + { + throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount); + } + break; + + default: + throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode); + } + + _rng = options.Rng; + } + + // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + private static bool ByteArraysEqual(byte[] a, byte[] b) + { + if (a == null && b == null) + { + return true; + } + if (a == null || b == null || a.Length != b.Length) + { + return false; + } + var areSame = true; + for (var i = 0; i < a.Length; i++) + { + areSame &= (a[i] == b[i]); + } + return areSame; + } + + /// + /// Returns a hashed representation of the supplied for the specified . + /// + /// The user whose password is to be hashed. + /// The password to hash. + /// A hashed representation of the supplied for the specified . + public virtual string HashPassword(TUser user, string password) + { + if (password == null) + { + throw new ArgumentNullException(nameof(password)); + } + + if (_compatibilityMode == PasswordHasherCompatibilityMode.IdentityV2) + { + return Convert.ToBase64String(HashPasswordV2(password, _rng)); + } + else + { + return Convert.ToBase64String(HashPasswordV3(password, _rng)); + } + } + + private static byte[] HashPasswordV2(string password, RandomNumberGenerator rng) + { + const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes + const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes + const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits + const int SaltSize = 128 / 8; // 128 bits + + // Produce a version 2 (see comment above) text hash. + byte[] salt = new byte[SaltSize]; + rng.GetBytes(salt); + byte[] subkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength); + + var outputBytes = new byte[1 + SaltSize + Pbkdf2SubkeyLength]; + outputBytes[0] = 0x00; // format marker + Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize); + Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, Pbkdf2SubkeyLength); + return outputBytes; + } + + private byte[] HashPasswordV3(string password, RandomNumberGenerator rng) + { + return HashPasswordV3(password, rng, + prf: KeyDerivationPrf.HMACSHA256, + iterCount: _iterCount, + saltSize: 128 / 8, + numBytesRequested: 256 / 8); + } + + private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested) + { + // Produce a version 3 (see comment above) text hash. + byte[] salt = new byte[saltSize]; + rng.GetBytes(salt); + byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested); + + var outputBytes = new byte[13 + salt.Length + subkey.Length]; + outputBytes[0] = 0x01; // format marker + WriteNetworkByteOrder(outputBytes, 1, (uint)prf); + WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount); + WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize); + Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length); + Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length); + return outputBytes; + } + + private static uint ReadNetworkByteOrder(byte[] buffer, int offset) + { + return ((uint)(buffer[offset + 0]) << 24) + | ((uint)(buffer[offset + 1]) << 16) + | ((uint)(buffer[offset + 2]) << 8) + | ((uint)(buffer[offset + 3])); + } + + /// + /// Returns a indicating the result of a password hash comparison. + /// + /// The user whose password should be verified. + /// The hash value for a user's stored password. + /// The password supplied for comparison. + /// A indicating the result of a password hash comparison. + /// Implementations of this method should be time consistent. + public virtual PasswordVerificationResult VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword) + { + if (hashedPassword == null) + { + throw new ArgumentNullException(nameof(hashedPassword)); + } + if (providedPassword == null) + { + throw new ArgumentNullException(nameof(providedPassword)); + } + + byte[] decodedHashedPassword = Convert.FromBase64String(hashedPassword); + + // read the format marker from the hashed password + if (decodedHashedPassword.Length == 0) + { + return PasswordVerificationResult.Failed; + } + switch (decodedHashedPassword[0]) + { + case 0x00: + if (VerifyHashedPasswordV2(decodedHashedPassword, providedPassword)) + { + // This is an old password hash format - the caller needs to rehash if we're not running in an older compat mode. + return (_compatibilityMode == PasswordHasherCompatibilityMode.IdentityV3) + ? PasswordVerificationResult.SuccessRehashNeeded + : PasswordVerificationResult.Success; + } + else + { + return PasswordVerificationResult.Failed; + } + + case 0x01: + int embeddedIterCount; + if (VerifyHashedPasswordV3(decodedHashedPassword, providedPassword, out embeddedIterCount)) + { + // If this hasher was configured with a higher iteration count, change the entry now. + return (embeddedIterCount < _iterCount) + ? PasswordVerificationResult.SuccessRehashNeeded + : PasswordVerificationResult.Success; + } + else + { + return PasswordVerificationResult.Failed; + } + + default: + return PasswordVerificationResult.Failed; // unknown format marker + } + } + + private static bool VerifyHashedPasswordV2(byte[] hashedPassword, string password) + { + const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes + const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes + const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits + const int SaltSize = 128 / 8; // 128 bits + + // We know ahead of time the exact length of a valid hashed password payload. + if (hashedPassword.Length != 1 + SaltSize + Pbkdf2SubkeyLength) + { + return false; // bad size + } + + byte[] salt = new byte[SaltSize]; + Buffer.BlockCopy(hashedPassword, 1, salt, 0, salt.Length); + + byte[] expectedSubkey = new byte[Pbkdf2SubkeyLength]; + Buffer.BlockCopy(hashedPassword, 1 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); + + // Hash the incoming password and verify it + byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength); + return ByteArraysEqual(actualSubkey, expectedSubkey); + } + + private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, out int iterCount) + { + iterCount = default(int); + + try + { + // Read header information + KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1); + iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5); + int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); + + // Read the salt: must be >= 128 bits + if (saltLength < 128 / 8) + { + return false; + } + byte[] salt = new byte[saltLength]; + Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length); + + // Read the subkey (the rest of the payload): must be >= 128 bits + int subkeyLength = hashedPassword.Length - 13 - salt.Length; + if (subkeyLength < 128 / 8) + { + return false; + } + byte[] expectedSubkey = new byte[subkeyLength]; + Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length); + + // Hash the incoming password and verify it + byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength); + return ByteArraysEqual(actualSubkey, expectedSubkey); + } + catch + { + // This should never occur except in the case of a malformed payload, where + // we might go off the end of the array. Regardless, a malformed payload + // implies verification failed. + return false; + } + } + + private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value) + { + buffer[offset + 0] = (byte)(value >> 24); + buffer[offset + 1] = (byte)(value >> 16); + buffer[offset + 2] = (byte)(value >> 8); + buffer[offset + 3] = (byte)(value >> 0); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PasswordHasherCompatibilityMode.cs b/src/Identity/src/Core/PasswordHasherCompatibilityMode.cs new file mode 100644 index 0000000000..4b401c7581 --- /dev/null +++ b/src/Identity/src/Core/PasswordHasherCompatibilityMode.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Specifies the format used for hashing passwords. + /// + public enum PasswordHasherCompatibilityMode + { + /// + /// Indicates hashing passwords in a way that is compatible with ASP.NET Identity versions 1 and 2. + /// + IdentityV2, + + /// + /// Indicates hashing passwords in a way that is compatible with ASP.NET Identity version 3. + /// + IdentityV3 + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PasswordHasherOptions.cs b/src/Identity/src/Core/PasswordHasherOptions.cs new file mode 100644 index 0000000000..56ceb2ac06 --- /dev/null +++ b/src/Identity/src/Core/PasswordHasherOptions.cs @@ -0,0 +1,38 @@ +// 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.Security.Cryptography; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Specifies options for password hashing. + /// + public class PasswordHasherOptions + { + private static readonly RandomNumberGenerator _defaultRng = RandomNumberGenerator.Create(); // secure PRNG + + /// + /// Gets or sets the compatibility mode used when hashing passwords. Defaults to 'ASP.NET Identity version 3'. + /// + /// + /// The compatibility mode used when hashing passwords. + /// + public PasswordHasherCompatibilityMode CompatibilityMode { get; set; } = PasswordHasherCompatibilityMode.IdentityV3; + + /// + /// Gets or sets the number of iterations used when hashing passwords using PBKDF2. Default is 10,000. + /// + /// + /// The number of iterations used when hashing passwords using PBKDF2. + /// + /// + /// This value is only used when the compatibility mode is set to 'V3'. + /// The value must be a positive integer. + /// + public int IterationCount { get; set; } = 10000; + + // for unit testing + internal RandomNumberGenerator Rng { get; set; } = _defaultRng; + } +} diff --git a/src/Identity/src/Core/PasswordOptions.cs b/src/Identity/src/Core/PasswordOptions.cs new file mode 100644 index 0000000000..d532ea4fab --- /dev/null +++ b/src/Identity/src/Core/PasswordOptions.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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Specifies options for password requirements. + /// + public class PasswordOptions + { + /// + /// Gets or sets the minimum length a password must be. Defaults to 6. + /// + public int RequiredLength { get; set; } = 6; + + /// + /// Gets or sets the minimum number of unique chars a password must comprised of. Defaults to 1. + /// + public int RequiredUniqueChars { get; set; } = 1; + + /// + /// Gets or sets a flag indicating if passwords must contain a non-alphanumeric character. Defaults to true. + /// + /// True if passwords must contain a non-alphanumeric character, otherwise false. + public bool RequireNonAlphanumeric { get; set; } = true; + + /// + /// Gets or sets a flag indicating if passwords must contain a lower case ASCII character. Defaults to true. + /// + /// True if passwords must contain a lower case ASCII character. + public bool RequireLowercase { get; set; } = true; + + /// + /// Gets or sets a flag indicating if passwords must contain a upper case ASCII character. Defaults to true. + /// + /// True if passwords must contain a upper case ASCII character. + public bool RequireUppercase { get; set; } = true; + + /// + /// Gets or sets a flag indicating if passwords must contain a digit. Defaults to true. + /// + /// True if passwords must contain a digit. + public bool RequireDigit { get; set; } = true; + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PasswordValidator.cs b/src/Identity/src/Core/PasswordValidator.cs new file mode 100644 index 0000000000..672e09e2f3 --- /dev/null +++ b/src/Identity/src/Core/PasswordValidator.cs @@ -0,0 +1,121 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the default password policy for Identity. + /// + /// The type that represents a user. + public class PasswordValidator : IPasswordValidator where TUser : class + { + /// + /// Constructions a new instance of . + /// + /// The to retrieve error text from. + public PasswordValidator(IdentityErrorDescriber errors = null) + { + Describer = errors ?? new IdentityErrorDescriber(); + } + + /// + /// Gets the used to supply error text. + /// + /// The used to supply error text. + public IdentityErrorDescriber Describer { get; private set; } + + /// + /// Validates a password as an asynchronous operation. + /// + /// The to retrieve the properties from. + /// The user whose password should be validated. + /// The password supplied for validation + /// The task object representing the asynchronous operation. + public virtual Task ValidateAsync(UserManager manager, TUser user, string password) + { + if (password == null) + { + throw new ArgumentNullException(nameof(password)); + } + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + var errors = new List(); + var options = manager.Options.Password; + if (string.IsNullOrWhiteSpace(password) || password.Length < options.RequiredLength) + { + errors.Add(Describer.PasswordTooShort(options.RequiredLength)); + } + if (options.RequireNonAlphanumeric && password.All(IsLetterOrDigit)) + { + errors.Add(Describer.PasswordRequiresNonAlphanumeric()); + } + if (options.RequireDigit && !password.Any(IsDigit)) + { + errors.Add(Describer.PasswordRequiresDigit()); + } + if (options.RequireLowercase && !password.Any(IsLower)) + { + errors.Add(Describer.PasswordRequiresLower()); + } + if (options.RequireUppercase && !password.Any(IsUpper)) + { + errors.Add(Describer.PasswordRequiresUpper()); + } + if (options.RequiredUniqueChars >= 1 && password.Distinct().Count() < options.RequiredUniqueChars) + { + errors.Add(Describer.PasswordRequiresUniqueChars(options.RequiredUniqueChars)); + } + return + Task.FromResult(errors.Count == 0 + ? IdentityResult.Success + : IdentityResult.Failed(errors.ToArray())); + } + + /// + /// Returns a flag indicating whether the supplied character is a digit. + /// + /// The character to check if it is a digit. + /// True if the character is a digit, otherwise false. + public virtual bool IsDigit(char c) + { + return c >= '0' && c <= '9'; + } + + /// + /// Returns a flag indicating whether the supplied character is a lower case ASCII letter. + /// + /// The character to check if it is a lower case ASCII letter. + /// True if the character is a lower case ASCII letter, otherwise false. + public virtual bool IsLower(char c) + { + return c >= 'a' && c <= 'z'; + } + + /// + /// Returns a flag indicating whether the supplied character is an upper case ASCII letter. + /// + /// The character to check if it is an upper case ASCII letter. + /// True if the character is an upper case ASCII letter, otherwise false. + public virtual bool IsUpper(char c) + { + return c >= 'A' && c <= 'Z'; + } + + /// + /// Returns a flag indicating whether the supplied character is an ASCII letter or digit. + /// + /// The character to check if it is an ASCII letter or digit. + /// True if the character is an ASCII letter or digit, otherwise false. + public virtual bool IsLetterOrDigit(char c) + { + return IsUpper(c) || IsLower(c) || IsDigit(c); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PasswordVerificationResult.cs b/src/Identity/src/Core/PasswordVerificationResult.cs new file mode 100644 index 0000000000..49242d1b1e --- /dev/null +++ b/src/Identity/src/Core/PasswordVerificationResult.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Specifies the results for password verification. + /// + public enum PasswordVerificationResult + { + /// + /// Indicates password verification failed. + /// + Failed = 0, + + /// + /// Indicates password verification was successful. + /// + Success = 1, + + /// + /// Indicates password verification was successful however the password was encoded using a deprecated algorithm + /// and should be rehashed and updated. + /// + SuccessRehashNeeded = 2 + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PersonalDataAttribute.cs b/src/Identity/src/Core/PersonalDataAttribute.cs new file mode 100644 index 0000000000..5ee23537fa --- /dev/null +++ b/src/Identity/src/Core/PersonalDataAttribute.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to indicate that a something is considered personal data. + /// + public class PersonalDataAttribute : Attribute + { } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PhoneNumberTokenProvider.cs b/src/Identity/src/Core/PhoneNumberTokenProvider.cs new file mode 100644 index 0000000000..b3c497f867 --- /dev/null +++ b/src/Identity/src/Core/PhoneNumberTokenProvider.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a token provider that generates tokens from a user's security stamp and + /// sends them to the user via their phone number. + /// + /// The type encapsulating a user. + public class PhoneNumberTokenProvider : TotpSecurityStampBasedTokenProvider + where TUser : class + { + /// + /// Returns a flag indicating whether the token provider can generate a token suitable for two factor authentication token for + /// the specified . + /// + /// The that can be used to retrieve user properties. + /// The user a token could be generated for. + /// + /// The that represents the asynchronous operation, containing the a flag indicating if a two + /// factor token could be generated by this provider for the specified . + /// The task will return true if a two factor authentication token could be generated as the user has + /// a telephone number, otherwise false. + /// + public override async Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + var phoneNumber = await manager.GetPhoneNumberAsync(user); + return !string.IsNullOrWhiteSpace(phoneNumber) && await manager.IsPhoneNumberConfirmedAsync(user); + } + + /// + /// Returns a constant, provider and user unique modifier used for entropy in generated tokens from user information. + /// + /// The purpose the token will be generated for. + /// The that can be used to retrieve user properties. + /// The user a token should be generated for. + /// + /// The that represents the asynchronous operation, containing a constant modifier for the specified + /// and . + /// + public override async Task GetUserModifierAsync(string purpose, UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + var phoneNumber = await manager.GetPhoneNumberAsync(user); + return "PhoneNumber:" + purpose + ":" + phoneNumber; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/PrincipalExtensions.cs b/src/Identity/src/Core/PrincipalExtensions.cs new file mode 100644 index 0000000000..fc33e19cf8 --- /dev/null +++ b/src/Identity/src/Core/PrincipalExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.AspNetCore.Identity; + +namespace System.Security.Claims +{ + /// + /// Claims related extensions for . + /// + public static class PrincipalExtensions + { + /// + /// Returns the value for the first claim of the specified type otherwise null the claim is not present. + /// + /// The instance this method extends. + /// The claim type whose first value should be returned. + /// The value of the first instance of the specified claim type, or null if the claim is not present. + public static string FindFirstValue(this ClaimsPrincipal principal, string claimType) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + var claim = principal.FindFirst(claimType); + return claim != null ? claim.Value : null; + } + + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/Properties/AssemblyInfo.cs b/src/Identity/src/Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c81ac16167 --- /dev/null +++ b/src/Identity/src/Core/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +// 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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Specification.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.InMemory.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Identity/src/Core/Properties/Resources.Designer.cs b/src/Identity/src/Core/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..e39a1c63c8 --- /dev/null +++ b/src/Identity/src/Core/Properties/Resources.Designer.cs @@ -0,0 +1,730 @@ +// +namespace Microsoft.Extensions.Identity.Core +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.Extensions.Identity.Core.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// Optimistic concurrency failure, object has been modified. + /// + internal static string ConcurrencyFailure + { + get => GetString("ConcurrencyFailure"); + } + + /// + /// Optimistic concurrency failure, object has been modified. + /// + internal static string FormatConcurrencyFailure() + => GetString("ConcurrencyFailure"); + + /// + /// An unknown failure has occurred. + /// + internal static string DefaultError + { + get => GetString("DefaultError"); + } + + /// + /// An unknown failure has occurred. + /// + internal static string FormatDefaultError() + => GetString("DefaultError"); + + /// + /// Email '{0}' is already taken. + /// + internal static string DuplicateEmail + { + get => GetString("DuplicateEmail"); + } + + /// + /// Email '{0}' is already taken. + /// + internal static string FormatDuplicateEmail(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateEmail"), p0); + + /// + /// Role name '{0}' is already taken. + /// + internal static string DuplicateRoleName + { + get => GetString("DuplicateRoleName"); + } + + /// + /// Role name '{0}' is already taken. + /// + internal static string FormatDuplicateRoleName(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateRoleName"), p0); + + /// + /// User name '{0}' is already taken. + /// + internal static string DuplicateUserName + { + get => GetString("DuplicateUserName"); + } + + /// + /// User name '{0}' is already taken. + /// + internal static string FormatDuplicateUserName(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUserName"), p0); + + /// + /// Email '{0}' is invalid. + /// + internal static string InvalidEmail + { + get => GetString("InvalidEmail"); + } + + /// + /// Email '{0}' is invalid. + /// + internal static string FormatInvalidEmail(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidEmail"), p0); + + /// + /// Type {0} must derive from {1}<{2}>. + /// + internal static string InvalidManagerType + { + get => GetString("InvalidManagerType"); + } + + /// + /// Type {0} must derive from {1}<{2}>. + /// + internal static string FormatInvalidManagerType(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2); + + /// + /// The provided PasswordHasherCompatibilityMode is invalid. + /// + internal static string InvalidPasswordHasherCompatibilityMode + { + get => GetString("InvalidPasswordHasherCompatibilityMode"); + } + + /// + /// The provided PasswordHasherCompatibilityMode is invalid. + /// + internal static string FormatInvalidPasswordHasherCompatibilityMode() + => GetString("InvalidPasswordHasherCompatibilityMode"); + + /// + /// The iteration count must be a positive integer. + /// + internal static string InvalidPasswordHasherIterationCount + { + get => GetString("InvalidPasswordHasherIterationCount"); + } + + /// + /// The iteration count must be a positive integer. + /// + internal static string FormatInvalidPasswordHasherIterationCount() + => GetString("InvalidPasswordHasherIterationCount"); + + /// + /// Role name '{0}' is invalid. + /// + internal static string InvalidRoleName + { + get => GetString("InvalidRoleName"); + } + + /// + /// Role name '{0}' is invalid. + /// + internal static string FormatInvalidRoleName(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRoleName"), p0); + + /// + /// Invalid token. + /// + internal static string InvalidToken + { + get => GetString("InvalidToken"); + } + + /// + /// Invalid token. + /// + internal static string FormatInvalidToken() + => GetString("InvalidToken"); + + /// + /// User name '{0}' is invalid, can only contain letters or digits. + /// + internal static string InvalidUserName + { + get => GetString("InvalidUserName"); + } + + /// + /// User name '{0}' is invalid, can only contain letters or digits. + /// + internal static string FormatInvalidUserName(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidUserName"), p0); + + /// + /// A user with this login already exists. + /// + internal static string LoginAlreadyAssociated + { + get => GetString("LoginAlreadyAssociated"); + } + + /// + /// A user with this login already exists. + /// + internal static string FormatLoginAlreadyAssociated() + => GetString("LoginAlreadyAssociated"); + + /// + /// AddIdentity must be called on the service collection. + /// + internal static string MustCallAddIdentity + { + get => GetString("MustCallAddIdentity"); + } + + /// + /// AddIdentity must be called on the service collection. + /// + internal static string FormatMustCallAddIdentity() + => GetString("MustCallAddIdentity"); + + /// + /// No IUserTwoFactorTokenProvider<{0}> named '{1}' is registered. + /// + internal static string NoTokenProvider + { + get => GetString("NoTokenProvider"); + } + + /// + /// No IUserTwoFactorTokenProvider<{0}> named '{1}' is registered. + /// + internal static string FormatNoTokenProvider(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0, p1); + + /// + /// User security stamp cannot be null. + /// + internal static string NullSecurityStamp + { + get => GetString("NullSecurityStamp"); + } + + /// + /// User security stamp cannot be null. + /// + internal static string FormatNullSecurityStamp() + => GetString("NullSecurityStamp"); + + /// + /// Incorrect password. + /// + internal static string PasswordMismatch + { + get => GetString("PasswordMismatch"); + } + + /// + /// Incorrect password. + /// + internal static string FormatPasswordMismatch() + => GetString("PasswordMismatch"); + + /// + /// Passwords must have at least one digit ('0'-'9'). + /// + internal static string PasswordRequiresDigit + { + get => GetString("PasswordRequiresDigit"); + } + + /// + /// Passwords must have at least one digit ('0'-'9'). + /// + internal static string FormatPasswordRequiresDigit() + => GetString("PasswordRequiresDigit"); + + /// + /// Passwords must have at least one lowercase ('a'-'z'). + /// + internal static string PasswordRequiresLower + { + get => GetString("PasswordRequiresLower"); + } + + /// + /// Passwords must have at least one lowercase ('a'-'z'). + /// + internal static string FormatPasswordRequiresLower() + => GetString("PasswordRequiresLower"); + + /// + /// Passwords must have at least one non alphanumeric character. + /// + internal static string PasswordRequiresNonAlphanumeric + { + get => GetString("PasswordRequiresNonAlphanumeric"); + } + + /// + /// Passwords must have at least one non alphanumeric character. + /// + internal static string FormatPasswordRequiresNonAlphanumeric() + => GetString("PasswordRequiresNonAlphanumeric"); + + /// + /// Passwords must have at least one uppercase ('A'-'Z'). + /// + internal static string PasswordRequiresUpper + { + get => GetString("PasswordRequiresUpper"); + } + + /// + /// Passwords must have at least one uppercase ('A'-'Z'). + /// + internal static string FormatPasswordRequiresUpper() + => GetString("PasswordRequiresUpper"); + + /// + /// Passwords must be at least {0} characters. + /// + internal static string PasswordTooShort + { + get => GetString("PasswordTooShort"); + } + + /// + /// Passwords must be at least {0} characters. + /// + internal static string FormatPasswordTooShort(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("PasswordTooShort"), p0); + + /// + /// Role {0} does not exist. + /// + internal static string RoleNotFound + { + get => GetString("RoleNotFound"); + } + + /// + /// Role {0} does not exist. + /// + internal static string FormatRoleNotFound(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0); + + /// + /// Store does not implement IQueryableRoleStore<TRole>. + /// + internal static string StoreNotIQueryableRoleStore + { + get => GetString("StoreNotIQueryableRoleStore"); + } + + /// + /// Store does not implement IQueryableRoleStore<TRole>. + /// + internal static string FormatStoreNotIQueryableRoleStore() + => GetString("StoreNotIQueryableRoleStore"); + + /// + /// Store does not implement IQueryableUserStore<TUser>. + /// + internal static string StoreNotIQueryableUserStore + { + get => GetString("StoreNotIQueryableUserStore"); + } + + /// + /// Store does not implement IQueryableUserStore<TUser>. + /// + internal static string FormatStoreNotIQueryableUserStore() + => GetString("StoreNotIQueryableUserStore"); + + /// + /// Store does not implement IRoleClaimStore<TRole>. + /// + internal static string StoreNotIRoleClaimStore + { + get => GetString("StoreNotIRoleClaimStore"); + } + + /// + /// Store does not implement IRoleClaimStore<TRole>. + /// + internal static string FormatStoreNotIRoleClaimStore() + => GetString("StoreNotIRoleClaimStore"); + + /// + /// Store does not implement IUserAuthenticationTokenStore<User>. + /// + internal static string StoreNotIUserAuthenticationTokenStore + { + get => GetString("StoreNotIUserAuthenticationTokenStore"); + } + + /// + /// Store does not implement IUserAuthenticationTokenStore<User>. + /// + internal static string FormatStoreNotIUserAuthenticationTokenStore() + => GetString("StoreNotIUserAuthenticationTokenStore"); + + /// + /// Store does not implement IUserClaimStore<TUser>. + /// + internal static string StoreNotIUserClaimStore + { + get => GetString("StoreNotIUserClaimStore"); + } + + /// + /// Store does not implement IUserClaimStore<TUser>. + /// + internal static string FormatStoreNotIUserClaimStore() + => GetString("StoreNotIUserClaimStore"); + + /// + /// Store does not implement IUserConfirmationStore<TUser>. + /// + internal static string StoreNotIUserConfirmationStore + { + get => GetString("StoreNotIUserConfirmationStore"); + } + + /// + /// Store does not implement IUserConfirmationStore<TUser>. + /// + internal static string FormatStoreNotIUserConfirmationStore() + => GetString("StoreNotIUserConfirmationStore"); + + /// + /// Store does not implement IUserEmailStore<TUser>. + /// + internal static string StoreNotIUserEmailStore + { + get => GetString("StoreNotIUserEmailStore"); + } + + /// + /// Store does not implement IUserEmailStore<TUser>. + /// + internal static string FormatStoreNotIUserEmailStore() + => GetString("StoreNotIUserEmailStore"); + + /// + /// Store does not implement IUserLockoutStore<TUser>. + /// + internal static string StoreNotIUserLockoutStore + { + get => GetString("StoreNotIUserLockoutStore"); + } + + /// + /// Store does not implement IUserLockoutStore<TUser>. + /// + internal static string FormatStoreNotIUserLockoutStore() + => GetString("StoreNotIUserLockoutStore"); + + /// + /// Store does not implement IUserLoginStore<TUser>. + /// + internal static string StoreNotIUserLoginStore + { + get => GetString("StoreNotIUserLoginStore"); + } + + /// + /// Store does not implement IUserLoginStore<TUser>. + /// + internal static string FormatStoreNotIUserLoginStore() + => GetString("StoreNotIUserLoginStore"); + + /// + /// Store does not implement IUserPasswordStore<TUser>. + /// + internal static string StoreNotIUserPasswordStore + { + get => GetString("StoreNotIUserPasswordStore"); + } + + /// + /// Store does not implement IUserPasswordStore<TUser>. + /// + internal static string FormatStoreNotIUserPasswordStore() + => GetString("StoreNotIUserPasswordStore"); + + /// + /// Store does not implement IUserPhoneNumberStore<TUser>. + /// + internal static string StoreNotIUserPhoneNumberStore + { + get => GetString("StoreNotIUserPhoneNumberStore"); + } + + /// + /// Store does not implement IUserPhoneNumberStore<TUser>. + /// + internal static string FormatStoreNotIUserPhoneNumberStore() + => GetString("StoreNotIUserPhoneNumberStore"); + + /// + /// Store does not implement IUserRoleStore<TUser>. + /// + internal static string StoreNotIUserRoleStore + { + get => GetString("StoreNotIUserRoleStore"); + } + + /// + /// Store does not implement IUserRoleStore<TUser>. + /// + internal static string FormatStoreNotIUserRoleStore() + => GetString("StoreNotIUserRoleStore"); + + /// + /// Store does not implement IUserSecurityStampStore<TUser>. + /// + internal static string StoreNotIUserSecurityStampStore + { + get => GetString("StoreNotIUserSecurityStampStore"); + } + + /// + /// Store does not implement IUserSecurityStampStore<TUser>. + /// + internal static string FormatStoreNotIUserSecurityStampStore() + => GetString("StoreNotIUserSecurityStampStore"); + + /// + /// Store does not implement IUserAuthenticatorKeyStore<User>. + /// + internal static string StoreNotIUserAuthenticatorKeyStore + { + get => GetString("StoreNotIUserAuthenticatorKeyStore"); + } + + /// + /// Store does not implement IUserAuthenticatorKeyStore<User>. + /// + internal static string FormatStoreNotIUserAuthenticatorKeyStore() + => GetString("StoreNotIUserAuthenticatorKeyStore"); + + /// + /// Store does not implement IUserTwoFactorStore<TUser>. + /// + internal static string StoreNotIUserTwoFactorStore + { + get => GetString("StoreNotIUserTwoFactorStore"); + } + + /// + /// Store does not implement IUserTwoFactorStore<TUser>. + /// + internal static string FormatStoreNotIUserTwoFactorStore() + => GetString("StoreNotIUserTwoFactorStore"); + + /// + /// Recovery code redemption failed. + /// + internal static string RecoveryCodeRedemptionFailed + { + get => GetString("RecoveryCodeRedemptionFailed"); + } + + /// + /// Recovery code redemption failed. + /// + internal static string FormatRecoveryCodeRedemptionFailed() + => GetString("RecoveryCodeRedemptionFailed"); + + /// + /// User already has a password set. + /// + internal static string UserAlreadyHasPassword + { + get => GetString("UserAlreadyHasPassword"); + } + + /// + /// User already has a password set. + /// + internal static string FormatUserAlreadyHasPassword() + => GetString("UserAlreadyHasPassword"); + + /// + /// User already in role '{0}'. + /// + internal static string UserAlreadyInRole + { + get => GetString("UserAlreadyInRole"); + } + + /// + /// User already in role '{0}'. + /// + internal static string FormatUserAlreadyInRole(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("UserAlreadyInRole"), p0); + + /// + /// User is locked out. + /// + internal static string UserLockedOut + { + get => GetString("UserLockedOut"); + } + + /// + /// User is locked out. + /// + internal static string FormatUserLockedOut() + => GetString("UserLockedOut"); + + /// + /// Lockout is not enabled for this user. + /// + internal static string UserLockoutNotEnabled + { + get => GetString("UserLockoutNotEnabled"); + } + + /// + /// Lockout is not enabled for this user. + /// + internal static string FormatUserLockoutNotEnabled() + => GetString("UserLockoutNotEnabled"); + + /// + /// User {0} does not exist. + /// + internal static string UserNameNotFound + { + get => GetString("UserNameNotFound"); + } + + /// + /// User {0} does not exist. + /// + internal static string FormatUserNameNotFound(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("UserNameNotFound"), p0); + + /// + /// User is not in role '{0}'. + /// + internal static string UserNotInRole + { + get => GetString("UserNotInRole"); + } + + /// + /// User is not in role '{0}'. + /// + internal static string FormatUserNotInRole(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("UserNotInRole"), p0); + + /// + /// Store does not implement IUserTwoFactorRecoveryCodeStore<User>. + /// + internal static string StoreNotIUserTwoFactorRecoveryCodeStore + { + get => GetString("StoreNotIUserTwoFactorRecoveryCodeStore"); + } + + /// + /// Store does not implement IUserTwoFactorRecoveryCodeStore<User>. + /// + internal static string FormatStoreNotIUserTwoFactorRecoveryCodeStore() + => GetString("StoreNotIUserTwoFactorRecoveryCodeStore"); + + /// + /// Passwords must use at least {0} different characters. + /// + internal static string PasswordRequiresUniqueChars + { + get => GetString("PasswordRequiresUniqueChars"); + } + + /// + /// Passwords must use at least {0} different characters. + /// + internal static string FormatPasswordRequiresUniqueChars(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("PasswordRequiresUniqueChars"), p0); + + /// + /// No RoleType was specified, try AddRoles<TRole>(). + /// + internal static string NoRoleType + { + get => GetString("NoRoleType"); + } + + /// + /// No RoleType was specified, try AddRoles<TRole>(). + /// + internal static string FormatNoRoleType() + => GetString("NoRoleType"); + + /// + /// Store does not implement IProtectedUserStore<TUser> which is required when ProtectPersonalData = true. + /// + internal static string StoreNotIProtectedUserStore + { + get => GetString("StoreNotIProtectedUserStore"); + } + + /// + /// Store does not implement IProtectedUserStore<TUser> which is required when ProtectPersonalData = true. + /// + internal static string FormatStoreNotIProtectedUserStore() + => GetString("StoreNotIProtectedUserStore"); + + /// + /// No IPersonalDataProtector service was registered, this is required when ProtectPersonalData = true. + /// + internal static string NoPersonalDataProtector + { + get => GetString("NoPersonalDataProtector"); + } + + /// + /// No IPersonalDataProtector service was registered, this is required when ProtectPersonalData = true. + /// + internal static string FormatNoPersonalDataProtector() + => GetString("NoPersonalDataProtector"); + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Identity/src/Core/ProtectedPersonalDataAttribute.cs b/src/Identity/src/Core/ProtectedPersonalDataAttribute.cs new file mode 100644 index 0000000000..36ab193a42 --- /dev/null +++ b/src/Identity/src/Core/ProtectedPersonalDataAttribute.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to indicate that a something is considered personal data and should be protected. + /// + public class ProtectedPersonalDataAttribute : PersonalDataAttribute + { } +} \ No newline at end of file diff --git a/src/Identity/src/Core/Resources.resx b/src/Identity/src/Core/Resources.resx new file mode 100644 index 0000000000..5b63832c57 --- /dev/null +++ b/src/Identity/src/Core/Resources.resx @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Optimistic concurrency failure, object has been modified. + Error when optimistic concurrency fails + + + An unknown failure has occurred. + Default identity result error message + + + Email '{0}' is already taken. + Error for duplicate emails + + + Role name '{0}' is already taken. + Error for duplicate roles + + + User name '{0}' is already taken. + Error for duplicate user names + + + Email '{0}' is invalid. + Invalid email + + + Type {0} must derive from {1}<{2}>. + Error when the manager type is not derived correctly + + + The provided PasswordHasherCompatibilityMode is invalid. + Error when the password hasher doesn't understand the format it's being asked to produce. + + + The iteration count must be a positive integer. + Error when the iteration count is < 1. + + + Role name '{0}' is invalid. + Error for invalid role names + + + Invalid token. + Error when a token is not recognized + + + User name '{0}' is invalid, can only contain letters or digits. + User names can only contain letters or digits + + + A user with this login already exists. + Error when a login already linked + + + AddIdentity must be called on the service collection. + Error when AddIdentity is not called + + + No IUserTwoFactorTokenProvider<{0}> named '{1}' is registered. + Error when there is no IUserTwoFactorTokenProvider + + + User security stamp cannot be null. + Error when a user's security stamp is null. + + + Incorrect password. + Error when a password doesn't match + + + Passwords must have at least one digit ('0'-'9'). + Error when passwords do not have a digit + + + Passwords must have at least one lowercase ('a'-'z'). + Error when passwords do not have a lowercase letter + + + Passwords must have at least one non alphanumeric character. + Error when password does not have enough non alphanumeric characters + + + Passwords must have at least one uppercase ('A'-'Z'). + Error when passwords do not have an uppercase letter + + + Passwords must be at least {0} characters. + Error message for passwords that are too short + + + Role {0} does not exist. + Error when a role does not exist + + + Store does not implement IQueryableRoleStore<TRole>. + Error when the store does not implement this interface + + + Store does not implement IQueryableUserStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IRoleClaimStore<TRole>. + Error when the store does not implement this interface + + + Store does not implement IUserAuthenticationTokenStore<User>. + Error when the store does not implement this interface + + + Store does not implement IUserClaimStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserConfirmationStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserEmailStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserLockoutStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserLoginStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserPasswordStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserPhoneNumberStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserRoleStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserSecurityStampStore<TUser>. + Error when the store does not implement this interface + + + Store does not implement IUserAuthenticatorKeyStore<User>. + Error when the store does not implement this interface + + + Store does not implement IUserTwoFactorStore<TUser>. + Error when the store does not implement this interface + + + Recovery code redemption failed. + Error when a recovery code is not redeemed. + + + User already has a password set. + Error when AddPasswordAsync called when a user already has a password + + + User already in role '{0}'. + Error when a user is already in a role + + + User is locked out. + Error when a user is locked out + + + Lockout is not enabled for this user. + Error when lockout is not enabled + + + User {0} does not exist. + Error when a user does not exist + + + User is not in role '{0}'. + Error when a user is not in the role + + + Store does not implement IUserTwoFactorRecoveryCodeStore<User>. + Error when the store does not implement this interface + + + Passwords must use at least {0} different characters. + Error message for passwords that are based on similar characters + + + No RoleType was specified, try AddRoles<TRole>(). + Error when the IdentityBuilder.RoleType was not specified + + + Store does not implement IProtectedUserStore<TUser> which is required when ProtectPersonalData = true. + Error when the store does not implement this interface + + + No IPersonalDataProtector service was registered, this is required when ProtectPersonalData = true. + Error when there is no IPersonalDataProtector + + \ No newline at end of file diff --git a/src/Identity/src/Core/Rfc6238AuthenticationService.cs b/src/Identity/src/Core/Rfc6238AuthenticationService.cs new file mode 100644 index 0000000000..9a58683d22 --- /dev/null +++ b/src/Identity/src/Core/Rfc6238AuthenticationService.cs @@ -0,0 +1,110 @@ +// 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.Diagnostics; +using System.Net; +using System.Security.Cryptography; + +namespace Microsoft.AspNetCore.Identity +{ + using System; + using System.Text; + + internal static class Rfc6238AuthenticationService + { + private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3); + private static readonly Encoding _encoding = new UTF8Encoding(false, true); + private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); + + // Generates a new 80-bit security token + public static byte[] GenerateRandomKey() + { + byte[] bytes = new byte[20]; + _rng.GetBytes(bytes); + return bytes; + } + + internal static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier) + { + // # of 0's = length of pin + const int Mod = 1000000; + + // See https://tools.ietf.org/html/rfc4226 + // We can add an optional modifier + var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber)); + var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier)); + + // Generate DT string + var offset = hash[hash.Length - 1] & 0xf; + Debug.Assert(offset + 4 < hash.Length); + var binaryCode = (hash[offset] & 0x7f) << 24 + | (hash[offset + 1] & 0xff) << 16 + | (hash[offset + 2] & 0xff) << 8 + | (hash[offset + 3] & 0xff); + + return binaryCode % Mod; + } + + private static byte[] ApplyModifier(byte[] input, string modifier) + { + if (String.IsNullOrEmpty(modifier)) + { + return input; + } + + var modifierBytes = _encoding.GetBytes(modifier); + var combined = new byte[checked(input.Length + modifierBytes.Length)]; + Buffer.BlockCopy(input, 0, combined, 0, input.Length); + Buffer.BlockCopy(modifierBytes, 0, combined, input.Length, modifierBytes.Length); + return combined; + } + + // More info: https://tools.ietf.org/html/rfc6238#section-4 + private static ulong GetCurrentTimeStepNumber() + { + var delta = DateTime.UtcNow - _unixEpoch; + return (ulong)(delta.Ticks / _timestep.Ticks); + } + + public static int GenerateCode(byte[] securityToken, string modifier = null) + { + if (securityToken == null) + { + throw new ArgumentNullException(nameof(securityToken)); + } + + // Allow a variance of no greater than 9 minutes in either direction + var currentTimeStep = GetCurrentTimeStepNumber(); + using (var hashAlgorithm = new HMACSHA1(securityToken)) + { + return ComputeTotp(hashAlgorithm, currentTimeStep, modifier); + } + } + + public static bool ValidateCode(byte[] securityToken, int code, string modifier = null) + { + if (securityToken == null) + { + throw new ArgumentNullException(nameof(securityToken)); + } + + // Allow a variance of no greater than 9 minutes in either direction + var currentTimeStep = GetCurrentTimeStepNumber(); + using (var hashAlgorithm = new HMACSHA1(securityToken)) + { + for (var i = -2; i <= 2; i++) + { + var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier); + if (computedTotp == code) + { + return true; + } + } + } + + // No match + return false; + } + } +} diff --git a/src/Identity/src/Core/RoleManager.cs b/src/Identity/src/Core/RoleManager.cs new file mode 100644 index 0000000000..ac93b9941e --- /dev/null +++ b/src/Identity/src/Core/RoleManager.cs @@ -0,0 +1,481 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Identity.Core; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the APIs for managing roles in a persistence store. + /// + /// The type encapsulating a role. + public class RoleManager : IDisposable where TRole : class + { + private bool _disposed; + + /// + /// The cancellation token used to cancel operations. + /// + protected virtual CancellationToken CancellationToken => CancellationToken.None; + + /// + /// Constructs a new instance of . + /// + /// The persistence store the manager will operate over. + /// A collection of validators for roles. + /// The normalizer to use when normalizing role names to keys. + /// The used to provider error messages. + /// The logger used to log messages, warnings and errors. + public RoleManager(IRoleStore store, + IEnumerable> roleValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + ILogger> logger) + { + if (store == null) + { + throw new ArgumentNullException(nameof(store)); + } + Store = store; + KeyNormalizer = keyNormalizer; + ErrorDescriber = errors; + Logger = logger; + + if (roleValidators != null) + { + foreach (var v in roleValidators) + { + RoleValidators.Add(v); + } + } + } + + /// + /// Gets the persistence store this instance operates over. + /// + /// The persistence store this instance operates over. + protected IRoleStore Store { get; private set; } + + /// + /// Gets the used to log messages from the manager. + /// + /// + /// The used to log messages from the manager. + /// + public virtual ILogger Logger { get; set; } + + /// + /// Gets a list of validators for roles to call before persistence. + /// + /// A list of validators for roles to call before persistence. + public IList> RoleValidators { get; } = new List>(); + + /// + /// Gets the used to provider error messages. + /// + /// + /// The used to provider error messages. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Gets the normalizer to use when normalizing role names to keys. + /// + /// + /// The normalizer to use when normalizing role names to keys. + /// + public ILookupNormalizer KeyNormalizer { get; set; } + + /// + /// Gets an IQueryable collection of Roles if the persistence store is an , + /// otherwise throws a . + /// + /// An IQueryable collection of Roles if the persistence store is an . + /// Thrown if the persistence store is not an . + /// + /// Callers to this property should use to ensure the backing role store supports + /// returning an IQueryable list of roles. + /// + public virtual IQueryable Roles + { + get + { + var queryableStore = Store as IQueryableRoleStore; + if (queryableStore == null) + { + throw new NotSupportedException(Resources.StoreNotIQueryableRoleStore); + } + return queryableStore.Roles; + } + } + + /// + /// Gets a flag indicating whether the underlying persistence store supports returning an collection of roles. + /// + /// + /// true if the underlying persistence store supports returning an collection of roles, otherwise false. + /// + public virtual bool SupportsQueryableRoles + { + get + { + ThrowIfDisposed(); + return Store is IQueryableRoleStore; + } + } + + /// + /// Gets a flag indicating whether the underlying persistence store supports s for roles. + /// + /// + /// true if the underlying persistence store supports s for roles, otherwise false. + /// + public virtual bool SupportsRoleClaims + { + get + { + ThrowIfDisposed(); + return Store is IRoleClaimStore; + } + } + + /// + /// Creates the specified in the persistence store. + /// + /// The role to create. + /// + /// The that represents the asynchronous operation. + /// + public virtual async Task CreateAsync(TRole role) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + var result = await ValidateRoleAsync(role); + if (!result.Succeeded) + { + return result; + } + await UpdateNormalizedRoleNameAsync(role); + result = await Store.CreateAsync(role, CancellationToken); + return result; + } + + /// + /// Updates the normalized name for the specified . + /// + /// The role whose normalized name needs to be updated. + /// + /// The that represents the asynchronous operation. + /// + public virtual async Task UpdateNormalizedRoleNameAsync(TRole role) + { + var name = await GetRoleNameAsync(role); + await Store.SetNormalizedRoleNameAsync(role, NormalizeKey(name), CancellationToken); + } + + /// + /// Updates the specified . + /// + /// The role to updated. + /// + /// The that represents the asynchronous operation, containing the for the update. + /// + public virtual Task UpdateAsync(TRole role) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + return UpdateRoleAsync(role); + } + + /// + /// Deletes the specified . + /// + /// The role to delete. + /// + /// The that represents the asynchronous operation, containing the for the delete. + /// + public virtual Task DeleteAsync(TRole role) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + return Store.DeleteAsync(role, CancellationToken); + } + + /// + /// Gets a flag indicating whether the specified exists. + /// + /// The role name whose existence should be checked. + /// + /// The that represents the asynchronous operation, containing true if the role name exists, otherwise false. + /// + public virtual async Task RoleExistsAsync(string roleName) + { + ThrowIfDisposed(); + if (roleName == null) + { + throw new ArgumentNullException(nameof(roleName)); + } + + return await FindByNameAsync(NormalizeKey(roleName)) != null; + } + + /// + /// Gets a normalized representation of the specified . + /// + /// The value to normalize. + /// A normalized representation of the specified . + public virtual string NormalizeKey(string key) + { + return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key); + } + + /// + /// Finds the role associated with the specified if any. + /// + /// The role ID whose role should be returned. + /// + /// The that represents the asynchronous operation, containing the role + /// associated with the specified + /// + public virtual Task FindByIdAsync(string roleId) + { + ThrowIfDisposed(); + return Store.FindByIdAsync(roleId, CancellationToken); + } + + /// + /// Gets the name of the specified . + /// + /// The role whose name should be retrieved. + /// + /// The that represents the asynchronous operation, containing the name of the + /// specified . + /// + public virtual Task GetRoleNameAsync(TRole role) + { + ThrowIfDisposed(); + return Store.GetRoleNameAsync(role, CancellationToken); + } + + /// + /// Sets the name of the specified . + /// + /// The role whose name should be set. + /// The name to set. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task SetRoleNameAsync(TRole role, string name) + { + ThrowIfDisposed(); + + await Store.SetRoleNameAsync(role, name, CancellationToken); + await UpdateNormalizedRoleNameAsync(role); + return IdentityResult.Success; + } + + /// + /// Gets the ID of the specified . + /// + /// The role whose ID should be retrieved. + /// + /// The that represents the asynchronous operation, containing the ID of the + /// specified . + /// + public virtual Task GetRoleIdAsync(TRole role) + { + ThrowIfDisposed(); + return Store.GetRoleIdAsync(role, CancellationToken); + } + + /// + /// Finds the role associated with the specified if any. + /// + /// The name of the role to be returned. + /// + /// The that represents the asynchronous operation, containing the role + /// associated with the specified + /// + public virtual Task FindByNameAsync(string roleName) + { + ThrowIfDisposed(); + if (roleName == null) + { + throw new ArgumentNullException(nameof(roleName)); + } + + return Store.FindByNameAsync(NormalizeKey(roleName), CancellationToken); + } + + /// + /// Adds a claim to a role. + /// + /// The role to add the claim to. + /// The claim to add. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddClaimAsync(TRole role, Claim claim) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + await claimStore.AddClaimAsync(role, claim, CancellationToken); + return await UpdateRoleAsync(role); + } + + /// + /// Removes a claim from a role. + /// + /// The role to remove the claim from. + /// The claim to remove. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemoveClaimAsync(TRole role, Claim claim) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + await claimStore.RemoveClaimAsync(role, claim, CancellationToken); + return await UpdateRoleAsync(role); + } + + /// + /// Gets a list of claims associated with the specified . + /// + /// The role whose claims should be returned. + /// + /// The that represents the asynchronous operation, containing the list of s + /// associated with the specified . + /// + public virtual Task> GetClaimsAsync(TRole role) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return claimStore.GetClaimsAsync(role, CancellationToken); + } + + /// + /// Releases all resources used by the role manager. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the role manager and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + Store.Dispose(); + } + _disposed = true; + } + + /// + /// Should return if validation is successful. This is + /// called before saving the role via Create or Update. + /// + /// The role + /// A representing whether validation was successful. + protected virtual async Task ValidateRoleAsync(TRole role) + { + var errors = new List(); + foreach (var v in RoleValidators) + { + var result = await v.ValidateAsync(this, role); + if (!result.Succeeded) + { + errors.AddRange(result.Errors); + } + } + if (errors.Count > 0) + { + Logger.LogWarning(0, "Role {roleId} validation failed: {errors}.", await GetRoleIdAsync(role), string.Join(";", errors.Select(e => e.Code))); + return IdentityResult.Failed(errors.ToArray()); + } + return IdentityResult.Success; + } + + /// + /// Called to update the role after validating and updating the normalized role name. + /// + /// The role. + /// Whether the operation was successful. + protected virtual async Task UpdateRoleAsync(TRole role) + { + var result = await ValidateRoleAsync(role); + if (!result.Succeeded) + { + return result; + } + await UpdateNormalizedRoleNameAsync(role); + return await Store.UpdateAsync(role, CancellationToken); + } + + // IRoleClaimStore methods + private IRoleClaimStore GetClaimStore() + { + var cast = Store as IRoleClaimStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIRoleClaimStore); + } + return cast; + } + + /// + /// Throws if this class has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + } +} diff --git a/src/Identity/src/Core/RoleValidator.cs b/src/Identity/src/Core/RoleValidator.cs new file mode 100644 index 0000000000..511fd3996d --- /dev/null +++ b/src/Identity/src/Core/RoleValidator.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the default validation of roles. + /// + /// The type encapsulating a role. + public class RoleValidator : IRoleValidator where TRole : class + { + /// + /// Creates a new instance of / + /// + /// The used to provider error messages. + public RoleValidator(IdentityErrorDescriber errors = null) + { + Describer = errors ?? new IdentityErrorDescriber(); + } + + private IdentityErrorDescriber Describer { get; set; } + + /// + /// Validates a role as an asynchronous operation. + /// + /// The managing the role store. + /// The role to validate. + /// A that represents the of the asynchronous validation. + public virtual async Task ValidateAsync(RoleManager manager, TRole role) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + var errors = new List(); + await ValidateRoleName(manager, role, errors); + if (errors.Count > 0) + { + return IdentityResult.Failed(errors.ToArray()); + } + return IdentityResult.Success; + } + + private async Task ValidateRoleName(RoleManager manager, TRole role, + ICollection errors) + { + var roleName = await manager.GetRoleNameAsync(role); + if (string.IsNullOrWhiteSpace(roleName)) + { + errors.Add(Describer.InvalidRoleName(roleName)); + } + else + { + var owner = await manager.FindByNameAsync(roleName); + if (owner != null && + !string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role))) + { + errors.Add(Describer.DuplicateRoleName(roleName)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/SignInOptions.cs b/src/Identity/src/Core/SignInOptions.cs new file mode 100644 index 0000000000..3ae19eb8a3 --- /dev/null +++ b/src/Identity/src/Core/SignInOptions.cs @@ -0,0 +1,23 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options for configuring sign in. + /// + public class SignInOptions + { + /// + /// Gets or sets a flag indicating whether a confirmed email address is required to sign in. Defaults to false. + /// + /// True if a user must have a confirmed email address before they can sign in, otherwise false. + public bool RequireConfirmedEmail { get; set; } + + /// + /// Gets or sets a flag indicating whether a confirmed telephone number is required to sign in. Defaults to false. + /// + /// True if a user must have a confirmed telephone number before they can sign in, otherwise false. + public bool RequireConfirmedPhoneNumber { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/SignInResult.cs b/src/Identity/src/Core/SignInResult.cs new file mode 100644 index 0000000000..faae170c3b --- /dev/null +++ b/src/Identity/src/Core/SignInResult.cs @@ -0,0 +1,89 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents the result of a sign-in operation. + /// + public class SignInResult + { + private static readonly SignInResult _success = new SignInResult { Succeeded = true }; + private static readonly SignInResult _failed = new SignInResult(); + private static readonly SignInResult _lockedOut = new SignInResult { IsLockedOut = true }; + private static readonly SignInResult _notAllowed = new SignInResult { IsNotAllowed = true }; + private static readonly SignInResult _twoFactorRequired = new SignInResult { RequiresTwoFactor = true }; + + /// + /// Returns a flag indication whether the sign-in was successful. + /// + /// True if the sign-in was successful, otherwise false. + public bool Succeeded { get; protected set; } + + /// + /// Returns a flag indication whether the user attempting to sign-in is locked out. + /// + /// True if the user attempting to sign-in is locked out, otherwise false. + public bool IsLockedOut { get; protected set; } + + /// + /// Returns a flag indication whether the user attempting to sign-in is not allowed to sign-in. + /// + /// True if the user attempting to sign-in is not allowed to sign-in, otherwise false. + public bool IsNotAllowed { get; protected set; } + + /// + /// Returns a flag indication whether the user attempting to sign-in requires two factor authentication. + /// + /// True if the user attempting to sign-in requires two factor authentication, otherwise false. + public bool RequiresTwoFactor { get; protected set; } + + /// + /// Returns a that represents a successful sign-in. + /// + /// A that represents a successful sign-in. + public static SignInResult Success => _success; + + /// + /// Returns a that represents a failed sign-in. + /// + /// A that represents a failed sign-in. + public static SignInResult Failed => _failed; + + /// + /// Returns a that represents a sign-in attempt that failed because + /// the user was logged out. + /// + /// A that represents sign-in attempt that failed due to the + /// user being locked out. + public static SignInResult LockedOut => _lockedOut; + + /// + /// Returns a that represents a sign-in attempt that failed because + /// the user is not allowed to sign-in. + /// + /// A that represents sign-in attempt that failed due to the + /// user is not allowed to sign-in. + public static SignInResult NotAllowed => _notAllowed; + + /// + /// Returns a that represents a sign-in attempt that needs two-factor + /// authentication. + /// + /// A that represents sign-in attempt that needs two-factor + /// authentication. + public static SignInResult TwoFactorRequired => _twoFactorRequired; + + /// + /// Converts the value of the current object to its equivalent string representation. + /// + /// A string representation of value of the current object. + public override string ToString() + { + return IsLockedOut ? "Lockedout" : + IsNotAllowed ? "NotAllowed" : + RequiresTwoFactor ? "RequiresTwoFactor" : + Succeeded ? "Succeeded" : "Failed"; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/StoreOptions.cs b/src/Identity/src/Core/StoreOptions.cs new file mode 100644 index 0000000000..78b74cc8cc --- /dev/null +++ b/src/Identity/src/Core/StoreOptions.cs @@ -0,0 +1,23 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used for store specific options + /// + public class StoreOptions + { + /// + /// If set to a positive number, the default OnModelCreating will use this value as the max length for any + /// properties used as keys, i.e. UserId, LoginProvider, ProviderKey. + /// + public int MaxLengthForKeys { get; set; } + + /// + /// If set to true, the store must protect all personally identifying data for a user. + /// This will be enforced by requiring the store to implement . + /// + public bool ProtectPersonalData { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/TokenOptions.cs b/src/Identity/src/Core/TokenOptions.cs new file mode 100644 index 0000000000..8c969769a1 --- /dev/null +++ b/src/Identity/src/Core/TokenOptions.cs @@ -0,0 +1,79 @@ +// 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; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options for user tokens. + /// + public class TokenOptions + { + /// + /// Default token provider name used by email confirmation, password reset, and change email. + /// + public static readonly string DefaultProvider = "Default"; + + /// + /// Default token provider name used by the email provider. />. + /// + public static readonly string DefaultEmailProvider = "Email"; + + /// + /// Default token provider name used by the phone provider. />. + /// + public static readonly string DefaultPhoneProvider = "Phone"; + + /// + /// Default token provider name used by the . + /// + public static readonly string DefaultAuthenticatorProvider = "Authenticator"; + + /// + /// Will be used to construct UserTokenProviders with the key used as the providerName. + /// + public Dictionary ProviderMap { get; set; } = new Dictionary(); + + /// + /// Gets or sets the token provider used to generate tokens used in account confirmation emails. + /// + /// + /// The used to generate tokens used in account confirmation emails. + /// + public string EmailConfirmationTokenProvider { get; set; } = DefaultProvider; + + /// + /// Gets or sets the used to generate tokens used in password reset emails. + /// + /// + /// The used to generate tokens used in password reset emails. + /// + public string PasswordResetTokenProvider { get; set; } = DefaultProvider; + + /// + /// Gets or sets the used to generate tokens used in email change confirmation emails. + /// + /// + /// The used to generate tokens used in email change confirmation emails. + /// + public string ChangeEmailTokenProvider { get; set; } = DefaultProvider; + + /// + /// Gets or sets the used to generate tokens used when changing phone numbers. + /// + /// + /// The used to generate tokens used when changing phone numbers. + /// + public string ChangePhoneNumberTokenProvider { get; set; } = DefaultPhoneProvider; + + /// + /// Gets or sets the used to validate two factor sign ins with an authenticator. + /// + /// + /// The used to validate two factor sign ins with an authenticator. + /// + public string AuthenticatorTokenProvider { get; set; } = DefaultAuthenticatorProvider; + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/TokenProviderDescriptor.cs b/src/Identity/src/Core/TokenProviderDescriptor.cs new file mode 100644 index 0000000000..95c2920b3d --- /dev/null +++ b/src/Identity/src/Core/TokenProviderDescriptor.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to represents a token provider in 's TokenMap. + /// + public class TokenProviderDescriptor + { + /// + /// Initializes a new instance of the class. + /// + /// The concrete type for this token provider. + public TokenProviderDescriptor(Type type) + { + ProviderType = type; + } + + /// + /// The type that will be used for this token provider. + /// + public Type ProviderType { get; } + + /// + /// If specified, the instance to be used for the token provider. + /// + public object ProviderInstance { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/TotpSecurityStampBasedTokenProvider.cs b/src/Identity/src/Core/TotpSecurityStampBasedTokenProvider.cs new file mode 100644 index 0000000000..117fb0752f --- /dev/null +++ b/src/Identity/src/Core/TotpSecurityStampBasedTokenProvider.cs @@ -0,0 +1,106 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a token provider that generates time based codes using the user's security stamp. + /// + /// The type encapsulating a user. + public abstract class TotpSecurityStampBasedTokenProvider : IUserTwoFactorTokenProvider + where TUser : class + { + /// + /// Generates a token for the specified and . + /// + /// The purpose the token will be used for. + /// The that can be used to retrieve user properties. + /// The user a token should be generated for. + /// + /// The that represents the asynchronous operation, containing the token for the specified + /// and . + /// + /// + /// The parameter allows a token generator to be used for multiple types of token whilst + /// insuring a token for one purpose cannot be used for another. For example if you specified a purpose of "Email" + /// and validated it with the same purpose a token with the purpose of TOTP would not pass the check even if it was + /// for the same user. + /// + /// Implementations of should validate that purpose is not null or empty to + /// help with token separation. + /// + public virtual async Task GenerateAsync(string purpose, UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + var token = await manager.CreateSecurityTokenAsync(user); + var modifier = await GetUserModifierAsync(purpose, manager, user); + return Rfc6238AuthenticationService.GenerateCode(token, modifier).ToString("D6", CultureInfo.InvariantCulture); + } + + /// + /// Returns a flag indicating whether the specified is valid for the given + /// and . + /// + /// The purpose the token will be used for. + /// The token to validate. + /// The that can be used to retrieve user properties. + /// The user a token should be validated for. + /// + /// The that represents the asynchronous operation, containing the a flag indicating the result + /// of validating the for the specified and . + /// The task will return true if the token is valid, otherwise false. + /// + public virtual async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + int code; + if (!int.TryParse(token, out code)) + { + return false; + } + var securityToken = await manager.CreateSecurityTokenAsync(user); + var modifier = await GetUserModifierAsync(purpose, manager, user); + return securityToken != null && Rfc6238AuthenticationService.ValidateCode(securityToken, code, modifier); + } + + /// + /// Returns a constant, provider and user unique modifier used for entropy in generated tokens from user information. + /// + /// The purpose the token will be generated for. + /// The that can be used to retrieve user properties. + /// The user a token should be generated for. + /// + /// The that represents the asynchronous operation, containing a constant modifier for the specified + /// and . + /// + public virtual async Task GetUserModifierAsync(string purpose, UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + var userId = await manager.GetUserIdAsync(user); + return "Totp:" + purpose + ":" + userId; + } + + /// + /// Returns a flag indicating whether the token provider can generate a token suitable for two factor authentication token for + /// the specified . + /// + /// The that can be used to retrieve user properties. + /// The user a token could be generated for. + /// + /// The that represents the asynchronous operation, containing the a flag indicating if a two + /// factor token could be generated by this provider for the specified . + /// The task will return true if a two factor authentication token could be generated, otherwise false. + /// + public abstract Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user); + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/UpperInvariantLookupNormalizer.cs b/src/Identity/src/Core/UpperInvariantLookupNormalizer.cs new file mode 100644 index 0000000000..ec3fe26420 --- /dev/null +++ b/src/Identity/src/Core/UpperInvariantLookupNormalizer.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Implements by converting keys to their upper cased invariant culture representation. + /// + public class UpperInvariantLookupNormalizer : ILookupNormalizer + { + /// + /// Returns a normalized representation of the specified + /// by converting keys to their upper cased invariant culture representation. + /// + /// The key to normalize. + /// A normalized representation of the specified . + public virtual string Normalize(string key) + { + if (key == null) + { + return null; + } + return key.Normalize().ToUpperInvariant(); + } + } +} diff --git a/src/Identity/src/Core/UserClaimsPrincipalFactory.cs b/src/Identity/src/Core/UserClaimsPrincipalFactory.cs new file mode 100644 index 0000000000..dd7e316423 --- /dev/null +++ b/src/Identity/src/Core/UserClaimsPrincipalFactory.cs @@ -0,0 +1,157 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides methods to create a claims principal for a given user. + /// + /// The type used to represent a user. + public class UserClaimsPrincipalFactory : IUserClaimsPrincipalFactory + where TUser : class + { + /// + /// Initializes a new instance of the class. + /// + /// The to retrieve user information from. + /// The configured . + public UserClaimsPrincipalFactory( + UserManager userManager, + IOptions optionsAccessor) + { + if (userManager == null) + { + throw new ArgumentNullException(nameof(userManager)); + } + if (optionsAccessor == null || optionsAccessor.Value == null) + { + throw new ArgumentNullException(nameof(optionsAccessor)); + } + UserManager = userManager; + Options = optionsAccessor.Value; + } + + /// + /// Gets the for this factory. + /// + /// + /// The current for this factory instance. + /// + public UserManager UserManager { get; private set; } + + /// + /// Gets the for this factory. + /// + /// + /// The current for this factory instance. + /// + public IdentityOptions Options { get; private set; } + + /// + /// Creates a from an user asynchronously. + /// + /// The user to create a from. + /// The that represents the asynchronous creation operation, containing the created . + public virtual async Task CreateAsync(TUser user) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var id = await GenerateClaimsAsync(user); + return new ClaimsPrincipal(id); + } + + /// + /// Generate the claims for a user. + /// + /// The user to create a from. + /// The that represents the asynchronous creation operation, containing the created . + protected virtual async Task GenerateClaimsAsync(TUser user) + { + var userId = await UserManager.GetUserIdAsync(user); + var userName = await UserManager.GetUserNameAsync(user); + var id = new ClaimsIdentity("Identity.Application", // REVIEW: Used to match Application scheme + Options.ClaimsIdentity.UserNameClaimType, + Options.ClaimsIdentity.RoleClaimType); + id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId)); + id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName)); + if (UserManager.SupportsUserSecurityStamp) + { + id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType, + await UserManager.GetSecurityStampAsync(user))); + } + if (UserManager.SupportsUserClaim) + { + id.AddClaims(await UserManager.GetClaimsAsync(user)); + } + return id; + } + } + + /// + /// Provides methods to create a claims principal for a given user. + /// + /// The type used to represent a user. + /// The type used to represent a role. + public class UserClaimsPrincipalFactory : UserClaimsPrincipalFactory + where TUser : class + where TRole : class + { + /// + /// Initializes a new instance of the class. + /// + /// The to retrieve user information from. + /// The to retrieve a user's roles from. + /// The configured . + public UserClaimsPrincipalFactory(UserManager userManager, RoleManager roleManager, IOptions options) + : base(userManager, options) + { + if (roleManager == null) + { + throw new ArgumentNullException(nameof(roleManager)); + } + RoleManager = roleManager; + } + + /// + /// Gets the for this factory. + /// + /// + /// The current for this factory instance. + /// + public RoleManager RoleManager { get; private set; } + + /// + /// Generate the claims for a user. + /// + /// The user to create a from. + /// The that represents the asynchronous creation operation, containing the created . + protected override async Task GenerateClaimsAsync(TUser user) + { + var id = await base.GenerateClaimsAsync(user); + if (UserManager.SupportsUserRole) + { + var roles = await UserManager.GetRolesAsync(user); + foreach (var roleName in roles) + { + id.AddClaim(new Claim(Options.ClaimsIdentity.RoleClaimType, roleName)); + if (RoleManager.SupportsRoleClaims) + { + var role = await RoleManager.FindByNameAsync(roleName); + if (role != null) + { + id.AddClaims(await RoleManager.GetClaimsAsync(role)); + } + } + } + } + return id; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/UserLoginInfo.cs b/src/Identity/src/Core/UserLoginInfo.cs new file mode 100644 index 0000000000..a05a12bb43 --- /dev/null +++ b/src/Identity/src/Core/UserLoginInfo.cs @@ -0,0 +1,52 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents login information and source for a user record. + /// + public class UserLoginInfo + { + /// + /// Creates a new instance of + /// + /// The provider associated with this login information. + /// The unique identifier for this user provided by the login provider. + /// The display name for this user provided by the login provider. + public UserLoginInfo(string loginProvider, string providerKey, string displayName) + { + LoginProvider = loginProvider; + ProviderKey = providerKey; + ProviderDisplayName = displayName; + } + + /// + /// Gets or sets the provider for this instance of . + /// + /// The provider for the this instance of + /// + /// Examples of the provider may be Local, Facebook, Google, etc. + /// + public string LoginProvider { get; set; } + + /// + /// Gets or sets the unique identifier for the user identity user provided by the login provider. + /// + /// + /// The unique identifier for the user identity user provided by the login provider. + /// + /// + /// This would be unique per provider, examples may be @microsoft as a Twitter provider key. + /// + public string ProviderKey { get; set; } + + /// + /// Gets or sets the display name for the provider. + /// + /// + /// The display name for the provider. + /// + public string ProviderDisplayName { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/UserManager.cs b/src/Identity/src/Core/UserManager.cs new file mode 100644 index 0000000000..ff5ac02ab8 --- /dev/null +++ b/src/Identity/src/Core/UserManager.cs @@ -0,0 +1,2591 @@ +// 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.Globalization; +using System.Linq; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Identity.Core; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the APIs for managing user in a persistence store. + /// + /// The type encapsulating a user. + public class UserManager : IDisposable where TUser : class + { + /// + /// The data protection purpose used for the reset password related methods. + /// + public const string ResetPasswordTokenPurpose = "ResetPassword"; + + /// + /// The data protection purpose used for the change phone number methods. + /// + public const string ChangePhoneNumberTokenPurpose = "ChangePhoneNumber"; + + /// + /// The data protection purpose used for the email confirmation related methods. + /// + public const string ConfirmEmailTokenPurpose = "EmailConfirmation"; + + private readonly Dictionary> _tokenProviders = + new Dictionary>(); + + private TimeSpan _defaultLockout = TimeSpan.Zero; + private bool _disposed; + private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); + private IServiceProvider _services; + + /// + /// The cancellation token used to cancel operations. + /// + protected virtual CancellationToken CancellationToken => CancellationToken.None; + + /// + /// Constructs a new instance of . + /// + /// The persistence store the manager will operate over. + /// The accessor used to access the . + /// The password hashing implementation to use when saving passwords. + /// A collection of to validate users against. + /// A collection of to validate passwords against. + /// The to use when generating index keys for users. + /// The used to provider error messages. + /// The used to resolve services. + /// The logger used to log messages, warnings and errors. + public UserManager(IUserStore store, + IOptions optionsAccessor, + IPasswordHasher passwordHasher, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, + ILogger> logger) + { + if (store == null) + { + throw new ArgumentNullException(nameof(store)); + } + Store = store; + Options = optionsAccessor?.Value ?? new IdentityOptions(); + PasswordHasher = passwordHasher; + KeyNormalizer = keyNormalizer; + ErrorDescriber = errors; + Logger = logger; + + if (userValidators != null) + { + foreach (var v in userValidators) + { + UserValidators.Add(v); + } + } + if (passwordValidators != null) + { + foreach (var v in passwordValidators) + { + PasswordValidators.Add(v); + } + } + + _services = services; + if (services != null) + { + foreach (var providerName in Options.Tokens.ProviderMap.Keys) + { + var description = Options.Tokens.ProviderMap[providerName]; + + var provider = (description.ProviderInstance ?? services.GetRequiredService(description.ProviderType)) + as IUserTwoFactorTokenProvider; + if (provider != null) + { + RegisterTokenProvider(providerName, provider); + } + } + } + + if (Options.Stores.ProtectPersonalData) + { + if (!(Store is IProtectedUserStore)) + { + throw new InvalidOperationException(Resources.StoreNotIProtectedUserStore); + } + if (services.GetService() == null) + { + throw new InvalidOperationException(Resources.NoPersonalDataProtector); + } + } + } + + /// + /// Gets or sets the persistence store the manager operates over. + /// + /// The persistence store the manager operates over. + protected internal IUserStore Store { get; set; } + + /// + /// The used to log messages from the manager. + /// + /// + /// The used to log messages from the manager. + /// + public virtual ILogger Logger { get; set; } + + /// + /// The used to hash passwords. + /// + public IPasswordHasher PasswordHasher { get; set; } + + /// + /// The used to validate users. + /// + public IList> UserValidators { get; } = new List>(); + + /// + /// The used to validate passwords. + /// + public IList> PasswordValidators { get; } = new List>(); + + /// + /// The used to normalize things like user and role names. + /// + public ILookupNormalizer KeyNormalizer { get; set; } + + /// + /// The used to generate error messages. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// The used to configure Identity. + /// + public IdentityOptions Options { get; set; } + + /// + /// Gets a flag indicating whether the backing user store supports authentication tokens. + /// + /// + /// true if the backing user store supports authentication tokens, otherwise false. + /// + public virtual bool SupportsUserAuthenticationTokens + { + get + { + ThrowIfDisposed(); + return Store is IUserAuthenticationTokenStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports a user authenticator. + /// + /// + /// true if the backing user store supports a user authenticator, otherwise false. + /// + public virtual bool SupportsUserAuthenticatorKey + { + get + { + ThrowIfDisposed(); + return Store is IUserAuthenticatorKeyStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports recovery codes. + /// + /// + /// true if the backing user store supports a user authenticator, otherwise false. + /// + public virtual bool SupportsUserTwoFactorRecoveryCodes + { + get + { + ThrowIfDisposed(); + return Store is IUserTwoFactorRecoveryCodeStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports two factor authentication. + /// + /// + /// true if the backing user store supports user two factor authentication, otherwise false. + /// + public virtual bool SupportsUserTwoFactor + { + get + { + ThrowIfDisposed(); + return Store is IUserTwoFactorStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user passwords. + /// + /// + /// true if the backing user store supports user passwords, otherwise false. + /// + public virtual bool SupportsUserPassword + { + get + { + ThrowIfDisposed(); + return Store is IUserPasswordStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports security stamps. + /// + /// + /// true if the backing user store supports user security stamps, otherwise false. + /// + public virtual bool SupportsUserSecurityStamp + { + get + { + ThrowIfDisposed(); + return Store is IUserSecurityStampStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user roles. + /// + /// + /// true if the backing user store supports user roles, otherwise false. + /// + public virtual bool SupportsUserRole + { + get + { + ThrowIfDisposed(); + return Store is IUserRoleStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports external logins. + /// + /// + /// true if the backing user store supports external logins, otherwise false. + /// + public virtual bool SupportsUserLogin + { + get + { + ThrowIfDisposed(); + return Store is IUserLoginStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user emails. + /// + /// + /// true if the backing user store supports user emails, otherwise false. + /// + public virtual bool SupportsUserEmail + { + get + { + ThrowIfDisposed(); + return Store is IUserEmailStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user telephone numbers. + /// + /// + /// true if the backing user store supports user telephone numbers, otherwise false. + /// + public virtual bool SupportsUserPhoneNumber + { + get + { + ThrowIfDisposed(); + return Store is IUserPhoneNumberStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user claims. + /// + /// + /// true if the backing user store supports user claims, otherwise false. + /// + public virtual bool SupportsUserClaim + { + get + { + ThrowIfDisposed(); + return Store is IUserClaimStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports user lock-outs. + /// + /// + /// true if the backing user store supports user lock-outs, otherwise false. + /// + public virtual bool SupportsUserLockout + { + get + { + ThrowIfDisposed(); + return Store is IUserLockoutStore; + } + } + + /// + /// Gets a flag indicating whether the backing user store supports returning + /// collections of information. + /// + /// + /// true if the backing user store supports returning collections of + /// information, otherwise false. + /// + public virtual bool SupportsQueryableUsers + { + get + { + ThrowIfDisposed(); + return Store is IQueryableUserStore; + } + } + + /// + /// Returns an IQueryable of users if the store is an IQueryableUserStore + /// + public virtual IQueryable Users + { + get + { + var queryableStore = Store as IQueryableUserStore; + if (queryableStore == null) + { + throw new NotSupportedException(Resources.StoreNotIQueryableUserStore); + } + return queryableStore.Users; + } + } + + /// + /// Releases all resources used by the user manager. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Returns the Name claim value if present otherwise returns null. + /// + /// The instance. + /// The Name claim value, or null if the claim is not present. + /// The Name claim is identified by . + public virtual string GetUserName(ClaimsPrincipal principal) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + return principal.FindFirstValue(Options.ClaimsIdentity.UserNameClaimType); + } + + /// + /// Returns the User ID claim value if present otherwise returns null. + /// + /// The instance. + /// The User ID claim value, or null if the claim is not present. + /// The User ID claim is identified by . + public virtual string GetUserId(ClaimsPrincipal principal) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + return principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType); + } + + /// + /// Returns the user corresponding to the IdentityOptions.ClaimsIdentity.UserIdClaimType claim in + /// the principal or null. + /// + /// The principal which contains the user id claim. + /// The user corresponding to the IdentityOptions.ClaimsIdentity.UserIdClaimType claim in + /// the principal or null + public virtual Task GetUserAsync(ClaimsPrincipal principal) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + var id = GetUserId(principal); + return id == null ? Task.FromResult(null) : FindByIdAsync(id); + } + + /// + /// Generates a value suitable for use in concurrency tracking. + /// + /// The user to generate the stamp for. + /// + /// The that represents the asynchronous operation, containing the security + /// stamp for the specified . + /// + public virtual Task GenerateConcurrencyStampAsync(TUser user) + { + return Task.FromResult(Guid.NewGuid().ToString()); + } + + /// + /// Creates the specified in the backing store with no password, + /// as an asynchronous operation. + /// + /// The user to create. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task CreateAsync(TUser user) + { + ThrowIfDisposed(); + await UpdateSecurityStampInternal(user); + var result = await ValidateUserAsync(user); + if (!result.Succeeded) + { + return result; + } + if (Options.Lockout.AllowedForNewUsers && SupportsUserLockout) + { + await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, CancellationToken); + } + await UpdateNormalizedUserNameAsync(user); + await UpdateNormalizedEmailAsync(user); + + return await Store.CreateAsync(user, CancellationToken); + } + + /// + /// Updates the specified in the backing store. + /// + /// The user to update. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual Task UpdateAsync(TUser user) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return UpdateUserAsync(user); + } + + /// + /// Deletes the specified from the backing store. + /// + /// The user to delete. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual Task DeleteAsync(TUser user) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return Store.DeleteAsync(user, CancellationToken); + } + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public virtual Task FindByIdAsync(string userId) + { + ThrowIfDisposed(); + return Store.FindByIdAsync(userId, CancellationToken); + } + + /// + /// Finds and returns a user, if any, who has the specified user name. + /// + /// The user name to search for. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public virtual async Task FindByNameAsync(string userName) + { + ThrowIfDisposed(); + if (userName == null) + { + throw new ArgumentNullException(nameof(userName)); + } + userName = NormalizeKey(userName); + + var user = await Store.FindByNameAsync(userName, CancellationToken); + + // Need to potentially check all keys + if (user == null && Options.Stores.ProtectPersonalData) + { + var keyRing = _services.GetService(); + var protector = _services.GetService(); + if (keyRing != null && protector != null) + { + foreach (var key in keyRing.GetAllKeyIds()) + { + var oldKey = protector.Protect(key, userName); + user = await Store.FindByNameAsync(oldKey, CancellationToken); + if (user != null) + { + return user; + } + } + } + } + return user; + } + + /// + /// Creates the specified in the backing store with given password, + /// as an asynchronous operation. + /// + /// The user to create. + /// The password for the user to hash and store. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task CreateAsync(TUser user, string password) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (password == null) + { + throw new ArgumentNullException(nameof(password)); + } + var result = await UpdatePasswordHash(passwordStore, user, password); + if (!result.Succeeded) + { + return result; + } + return await CreateAsync(user); + } + + /// + /// Normalize a key (user name, email) for consistent comparisons. + /// + /// The key to normalize. + /// A normalized value representing the specified . + public virtual string NormalizeKey(string key) + { + return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key); + } + + private string ProtectPersonalData(string data) + { + if (Options.Stores.ProtectPersonalData) + { + var keyRing = _services.GetService(); + var protector = _services.GetService(); + return protector.Protect(keyRing.CurrentKeyId, data); + } + return data; + } + + /// + /// Updates the normalized user name for the specified . + /// + /// The user whose user name should be normalized and updated. + /// The that represents the asynchronous operation. + public virtual async Task UpdateNormalizedUserNameAsync(TUser user) + { + var normalizedName = NormalizeKey(await GetUserNameAsync(user)); + normalizedName = ProtectPersonalData(normalizedName); + await Store.SetNormalizedUserNameAsync(user, normalizedName, CancellationToken); + } + + /// + /// Gets the user name for the specified . + /// + /// The user whose name should be retrieved. + /// The that represents the asynchronous operation, containing the name for the specified . + public virtual async Task GetUserNameAsync(TUser user) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await Store.GetUserNameAsync(user, CancellationToken); + } + + /// + /// Sets the given for the specified . + /// + /// The user whose name should be set. + /// The user name to set. + /// The that represents the asynchronous operation. + public virtual async Task SetUserNameAsync(TUser user, string userName) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await Store.SetUserNameAsync(user, userName, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Gets the user identifier for the specified . + /// + /// The user whose identifier should be retrieved. + /// The that represents the asynchronous operation, containing the identifier for the specified . + public virtual async Task GetUserIdAsync(TUser user) + { + ThrowIfDisposed(); + return await Store.GetUserIdAsync(user, CancellationToken); + } + + /// + /// Returns a flag indicating whether the given is valid for the + /// specified . + /// + /// The user whose password should be validated. + /// The password to validate + /// The that represents the asynchronous operation, containing true if + /// the specified matches the one store for the , + /// otherwise false. + public virtual async Task CheckPasswordAsync(TUser user, string password) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + return false; + } + + var result = await VerifyPasswordAsync(passwordStore, user, password); + if (result == PasswordVerificationResult.SuccessRehashNeeded) + { + await UpdatePasswordHash(passwordStore, user, password, validatePassword: false); + await UpdateUserAsync(user); + } + + var success = result != PasswordVerificationResult.Failed; + if (!success) + { + Logger.LogWarning(0, "Invalid password for user {userId}.", await GetUserIdAsync(user)); + } + return success; + } + + /// + /// Gets a flag indicating whether the specified has a password. + /// + /// The user to return a flag for, indicating whether they have a password or not. + /// + /// The that represents the asynchronous operation, returning true if the specified has a password + /// otherwise false. + /// + public virtual Task HasPasswordAsync(TUser user) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return passwordStore.HasPasswordAsync(user, CancellationToken); + } + + /// + /// Adds the to the specified only if the user + /// does not already have a password. + /// + /// The user whose password should be set. + /// The password to set. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddPasswordAsync(TUser user, string password) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var hash = await passwordStore.GetPasswordHashAsync(user, CancellationToken); + if (hash != null) + { + Logger.LogWarning(1, "User {userId} already has a password.", await GetUserIdAsync(user)); + return IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword()); + } + var result = await UpdatePasswordHash(passwordStore, user, password); + if (!result.Succeeded) + { + return result; + } + return await UpdateUserAsync(user); + } + + /// + /// Changes a user's password after confirming the specified is correct, + /// as an asynchronous operation. + /// + /// The user whose password should be set. + /// The current password to validate before changing. + /// The new password to set for the specified . + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ChangePasswordAsync(TUser user, string currentPassword, string newPassword) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + + if (await VerifyPasswordAsync(passwordStore, user, currentPassword) != PasswordVerificationResult.Failed) + { + var result = await UpdatePasswordHash(passwordStore, user, newPassword); + if (!result.Succeeded) + { + return result; + } + return await UpdateUserAsync(user); + } + Logger.LogWarning(2, "Change password failed for user {userId}.", await GetUserIdAsync(user)); + return IdentityResult.Failed(ErrorDescriber.PasswordMismatch()); + } + + /// + /// Removes a user's password. + /// + /// The user whose password should be removed. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemovePasswordAsync(TUser user) + { + ThrowIfDisposed(); + var passwordStore = GetPasswordStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await UpdatePasswordHash(passwordStore, user, null, validatePassword: false); + return await UpdateUserAsync(user); + } + + /// + /// Returns a indicating the result of a password hash comparison. + /// + /// The store containing a user's password. + /// The user whose password should be verified. + /// The password to verify. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + protected virtual async Task VerifyPasswordAsync(IUserPasswordStore store, TUser user, string password) + { + var hash = await store.GetPasswordHashAsync(user, CancellationToken); + if (hash == null) + { + return PasswordVerificationResult.Failed; + } + return PasswordHasher.VerifyHashedPassword(user, hash, password); + } + + /// + /// Get the security stamp for the specified . + /// + /// The user whose security stamp should be set. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + public virtual async Task GetSecurityStampAsync(TUser user) + { + ThrowIfDisposed(); + var securityStore = GetSecurityStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await securityStore.GetSecurityStampAsync(user, CancellationToken); + } + + /// + /// Regenerates the security stamp for the specified . + /// + /// The user whose security stamp should be regenerated. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + /// + /// Regenerating a security stamp will sign out any saved login for the user. + /// + public virtual async Task UpdateSecurityStampAsync(TUser user) + { + ThrowIfDisposed(); + GetSecurityStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Generates a password reset token for the specified , using + /// the configured password reset token provider. + /// + /// The user to generate a password reset token for. + /// The that represents the asynchronous operation, + /// containing a password reset token for the specified . + public virtual Task GeneratePasswordResetTokenAsync(TUser user) + { + ThrowIfDisposed(); + return GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose); + } + + /// + /// Resets the 's password to the specified after + /// validating the given password reset . + /// + /// The user whose password should be reset. + /// The password reset token to verify. + /// The new password to set if reset token verification succeeds. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ResetPasswordAsync(TUser user, string token, string newPassword) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + // Make sure the token is valid and the stamp matches + if (!await VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose, token)) + { + return IdentityResult.Failed(ErrorDescriber.InvalidToken()); + } + var result = await UpdatePasswordHash(user, newPassword, validatePassword: true); + if (!result.Succeeded) + { + return result; + } + return await UpdateUserAsync(user); + } + + /// + /// Retrieves the user associated with the specified external login provider and login provider key. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + public virtual Task FindByLoginAsync(string loginProvider, string providerKey) + { + ThrowIfDisposed(); + var loginStore = GetLoginStore(); + if (loginProvider == null) + { + throw new ArgumentNullException(nameof(loginProvider)); + } + if (providerKey == null) + { + throw new ArgumentNullException(nameof(providerKey)); + } + return loginStore.FindByLoginAsync(loginProvider, providerKey, CancellationToken); + } + + /// + /// Attempts to remove the provided external login information from the specified . + /// and returns a flag indicating whether the removal succeed or not. + /// + /// The user to remove the login information from. + /// The login provide whose information should be removed. + /// The key given by the external login provider for the specified user. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey) + { + ThrowIfDisposed(); + var loginStore = GetLoginStore(); + if (loginProvider == null) + { + throw new ArgumentNullException(nameof(loginProvider)); + } + if (providerKey == null) + { + throw new ArgumentNullException(nameof(providerKey)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Adds an external to the specified . + /// + /// The user to add the login to. + /// The external to add to the specified . + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login) + { + ThrowIfDisposed(); + var loginStore = GetLoginStore(); + if (login == null) + { + throw new ArgumentNullException(nameof(login)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey); + if (existingUser != null) + { + Logger.LogWarning(4, "AddLogin for user {userId} failed because it was already associated with another user.", await GetUserIdAsync(user)); + return IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated()); + } + await loginStore.AddLoginAsync(user, login, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + public virtual async Task> GetLoginsAsync(TUser user) + { + ThrowIfDisposed(); + var loginStore = GetLoginStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await loginStore.GetLoginsAsync(user, CancellationToken); + } + + /// + /// Adds the specified to the . + /// + /// The user to add the claim to. + /// The claim to add. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual Task AddClaimAsync(TUser user, Claim claim) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return AddClaimsAsync(user, new Claim[] { claim }); + } + + /// + /// Adds the specified to the . + /// + /// The user to add the claim to. + /// The claims to add. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddClaimsAsync(TUser user, IEnumerable claims) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await claimStore.AddClaimsAsync(user, claims, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Replaces the given on the specified with the + /// + /// The user to replace the claim on. + /// The claim to replace. + /// The new claim to replace the existing with. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + if (newClaim == null) + { + throw new ArgumentNullException(nameof(newClaim)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await claimStore.ReplaceClaimAsync(user, claim, newClaim, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Removes the specified from the given . + /// + /// The user to remove the specified from. + /// The to remove. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual Task RemoveClaimAsync(TUser user, Claim claim) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + return RemoveClaimsAsync(user, new Claim[] { claim }); + } + + /// + /// Removes the specified from the given . + /// + /// The user to remove the specified from. + /// A collection of s to remove. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemoveClaimsAsync(TUser user, IEnumerable claims) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + + await claimStore.RemoveClaimsAsync(user, claims, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Gets a list of s to be belonging to the specified as an asynchronous operation. + /// + /// The user whose claims to retrieve. + /// + /// A that represents the result of the asynchronous query, a list of s. + /// + public virtual async Task> GetClaimsAsync(TUser user) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await claimStore.GetClaimsAsync(user, CancellationToken); + } + + /// + /// Add the specified to the named role. + /// + /// The user to add to the named role. + /// The name of the role to add the user to. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddToRoleAsync(TUser user, string role) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var normalizedRole = NormalizeKey(role); + if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) + { + return await UserAlreadyInRoleError(user, role); + } + await userRoleStore.AddToRoleAsync(user, normalizedRole, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Add the specified to the named roles. + /// + /// The user to add to the named roles. + /// The name of the roles to add the user to. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task AddToRolesAsync(TUser user, IEnumerable roles) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (roles == null) + { + throw new ArgumentNullException(nameof(roles)); + } + + foreach (var role in roles.Distinct()) + { + var normalizedRole = NormalizeKey(role); + if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) + { + return await UserAlreadyInRoleError(user, role); + } + await userRoleStore.AddToRoleAsync(user, normalizedRole, CancellationToken); + } + return await UpdateUserAsync(user); + } + + /// + /// Removes the specified from the named role. + /// + /// The user to remove from the named role. + /// The name of the role to remove the user from. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemoveFromRoleAsync(TUser user, string role) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var normalizedRole = NormalizeKey(role); + if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) + { + return await UserNotInRoleError(user, role); + } + await userRoleStore.RemoveFromRoleAsync(user, normalizedRole, CancellationToken); + return await UpdateUserAsync(user); + } + + private async Task UserAlreadyInRoleError(TUser user, string role) + { + Logger.LogWarning(5, "User {userId} is already in role {role}.", await GetUserIdAsync(user), role); + return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)); + } + + private async Task UserNotInRoleError(TUser user, string role) + { + Logger.LogWarning(6, "User {userId} is not in role {role}.", await GetUserIdAsync(user), role); + return IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)); + } + + /// + /// Removes the specified from the named roles. + /// + /// The user to remove from the named roles. + /// The name of the roles to remove the user from. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task RemoveFromRolesAsync(TUser user, IEnumerable roles) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (roles == null) + { + throw new ArgumentNullException(nameof(roles)); + } + + foreach (var role in roles) + { + var normalizedRole = NormalizeKey(role); + if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken)) + { + return await UserNotInRoleError(user, role); + } + await userRoleStore.RemoveFromRoleAsync(user, normalizedRole, CancellationToken); + } + return await UpdateUserAsync(user); + } + + /// + /// Gets a list of role names the specified belongs to. + /// + /// The user whose role names to retrieve. + /// The that represents the asynchronous operation, containing a list of role names. + public virtual async Task> GetRolesAsync(TUser user) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await userRoleStore.GetRolesAsync(user, CancellationToken); + } + + /// + /// Returns a flag indicating whether the specified is a member of the give named role. + /// + /// The user whose role membership should be checked. + /// The name of the role to be checked. + /// + /// The that represents the asynchronous operation, containing a flag indicating whether the specified is + /// a member of the named role. + /// + public virtual async Task IsInRoleAsync(TUser user, string role) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await userRoleStore.IsInRoleAsync(user, NormalizeKey(role), CancellationToken); + } + + /// + /// Gets the email address for the specified . + /// + /// The user whose email should be returned. + /// The task object containing the results of the asynchronous operation, the email address for the specified . + public virtual async Task GetEmailAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetEmailStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetEmailAsync(user, CancellationToken); + } + + /// + /// Sets the address for a . + /// + /// The user whose email should be set. + /// The email to set. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task SetEmailAsync(TUser user, string email) + { + ThrowIfDisposed(); + var store = GetEmailStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await store.SetEmailAsync(user, email, CancellationToken); + await store.SetEmailConfirmedAsync(user, false, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Gets the user, if any, associated with the normalized value of the specified email address. + /// + /// The email address to return the user for. + /// + /// The task object containing the results of the asynchronous lookup operation, the user, if any, associated with a normalized value of the specified email address. + /// + public virtual async Task FindByEmailAsync(string email) + { + ThrowIfDisposed(); + var store = GetEmailStore(); + if (email == null) + { + throw new ArgumentNullException(nameof(email)); + } + + email = NormalizeKey(email); + var user = await store.FindByEmailAsync(email, CancellationToken); + + // Need to potentially check all keys + if (user == null && Options.Stores.ProtectPersonalData) + { + var keyRing = _services.GetService(); + var protector = _services.GetService(); + if (keyRing != null && protector != null) + { + foreach (var key in keyRing.GetAllKeyIds()) + { + var oldKey = protector.Protect(key, email); + user = await store.FindByEmailAsync(oldKey, CancellationToken); + if (user != null) + { + return user; + } + } + } + } + return user; + } + + /// + /// Updates the normalized email for the specified . + /// + /// The user whose email address should be normalized and updated. + /// The task object representing the asynchronous operation. + public virtual async Task UpdateNormalizedEmailAsync(TUser user) + { + var store = GetEmailStore(throwOnFail: false); + if (store != null) + { + var email = await GetEmailAsync(user); + await store.SetNormalizedEmailAsync(user, ProtectPersonalData(NormalizeKey(email)), CancellationToken); + } + } + + /// + /// Generates an email confirmation token for the specified user. + /// + /// The user to generate an email confirmation token for. + /// + /// The that represents the asynchronous operation, an email confirmation token. + /// + public virtual Task GenerateEmailConfirmationTokenAsync(TUser user) + { + ThrowIfDisposed(); + return GenerateUserTokenAsync(user, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose); + } + + /// + /// Validates that an email confirmation token matches the specified . + /// + /// The user to validate the token against. + /// The email confirmation token to validate. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ConfirmEmailAsync(TUser user, string token) + { + ThrowIfDisposed(); + var store = GetEmailStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (!await VerifyUserTokenAsync(user, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose, token)) + { + return IdentityResult.Failed(ErrorDescriber.InvalidToken()); + } + await store.SetEmailConfirmedAsync(user, true, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Gets a flag indicating whether the email address for the specified has been verified, true if the email address is verified otherwise + /// false. + /// + /// The user whose email confirmation status should be returned. + /// + /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified + /// has been confirmed or not. + /// + public virtual async Task IsEmailConfirmedAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetEmailStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetEmailConfirmedAsync(user, CancellationToken); + } + + /// + /// Generates an email change token for the specified user. + /// + /// The user to generate an email change token for. + /// The new email address. + /// + /// The that represents the asynchronous operation, an email change token. + /// + public virtual Task GenerateChangeEmailTokenAsync(TUser user, string newEmail) + { + ThrowIfDisposed(); + return GenerateUserTokenAsync(user, Options.Tokens.ChangeEmailTokenProvider, GetChangeEmailTokenPurpose(newEmail)); + } + + /// + /// Updates a users emails if the specified email change is valid for the user. + /// + /// The user whose email should be updated. + /// The new email address. + /// The change email token to be verified. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ChangeEmailAsync(TUser user, string newEmail, string token) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + // Make sure the token is valid and the stamp matches + if (!await VerifyUserTokenAsync(user, Options.Tokens.ChangeEmailTokenProvider, GetChangeEmailTokenPurpose(newEmail), token)) + { + return IdentityResult.Failed(ErrorDescriber.InvalidToken()); + } + var store = GetEmailStore(); + await store.SetEmailAsync(user, newEmail, CancellationToken); + await store.SetEmailConfirmedAsync(user, true, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Gets the telephone number, if any, for the specified . + /// + /// The user whose telephone number should be retrieved. + /// The that represents the asynchronous operation, containing the user's telephone number, if any. + public virtual async Task GetPhoneNumberAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetPhoneNumberStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetPhoneNumberAsync(user, CancellationToken); + } + + /// + /// Sets the phone number for the specified . + /// + /// The user whose phone number to set. + /// The phone number to set. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task SetPhoneNumberAsync(TUser user, string phoneNumber) + { + ThrowIfDisposed(); + var store = GetPhoneNumberStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); + await store.SetPhoneNumberConfirmedAsync(user, false, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Sets the phone number for the specified if the specified + /// change is valid. + /// + /// The user whose phone number to set. + /// The phone number to set. + /// The phone number confirmation token to validate. + /// + /// The that represents the asynchronous operation, containing the + /// of the operation. + /// + public virtual async Task ChangePhoneNumberAsync(TUser user, string phoneNumber, string token) + { + ThrowIfDisposed(); + var store = GetPhoneNumberStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber)) + { + Logger.LogWarning(7, "Change phone number for user {userId} failed with invalid token.", await GetUserIdAsync(user)); + return IdentityResult.Failed(ErrorDescriber.InvalidToken()); + } + await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); + await store.SetPhoneNumberConfirmedAsync(user, true, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Gets a flag indicating whether the specified 's telephone number has been confirmed. + /// + /// The user to return a flag for, indicating whether their telephone number is confirmed. + /// + /// The that represents the asynchronous operation, returning true if the specified has a confirmed + /// telephone number otherwise false. + /// + public virtual Task IsPhoneNumberConfirmedAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetPhoneNumberStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return store.GetPhoneNumberConfirmedAsync(user, CancellationToken); + } + + /// + /// Generates a telephone number change token for the specified user. + /// + /// The user to generate a telephone number token for. + /// The new phone number the validation token should be sent to. + /// + /// The that represents the asynchronous operation, containing the telephone change number token. + /// + public virtual Task GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber) + { + ThrowIfDisposed(); + return GenerateUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose + ":" + phoneNumber); + } + + /// + /// Returns a flag indicating whether the specified 's phone number change verification + /// token is valid for the given . + /// + /// The user to validate the token against. + /// The telephone number change token to validate. + /// The telephone number the token was generated for. + /// + /// The that represents the asynchronous operation, returning true if the + /// is valid, otherwise false. + /// + public virtual Task VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + // Make sure the token is valid and the stamp matches + return VerifyUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose+":"+ phoneNumber, token); + } + + /// + /// Returns a flag indicating whether the specified is valid for + /// the given and . + /// + /// The user to validate the token against. + /// The token provider used to generate the token. + /// The purpose the token should be generated for. + /// The token to validate + /// + /// The that represents the asynchronous operation, returning true if the + /// is valid, otherwise false. + /// + public virtual async Task VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (tokenProvider == null) + { + throw new ArgumentNullException(nameof(tokenProvider)); + } + + if (!_tokenProviders.ContainsKey(tokenProvider)) + { + throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); + } + // Make sure the token is valid + var result = await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user); + + if (!result) + { + Logger.LogWarning(9, "VerifyUserTokenAsync() failed with purpose: {purpose} for user {userId}.", purpose, await GetUserIdAsync(user)); + } + return result; + } + + /// + /// Generates a token for the given and . + /// + /// The purpose the token will be for. + /// The user the token will be for. + /// The provider which will generate the token. + /// + /// The that represents result of the asynchronous operation, a token for + /// the given user and purpose. + /// + public virtual Task GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (tokenProvider == null) + { + throw new ArgumentNullException(nameof(tokenProvider)); + } + if (!_tokenProviders.ContainsKey(tokenProvider)) + { + throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); + } + + return _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user); + } + + /// + /// Registers a token provider. + /// + /// The name of the provider to register. + /// The provider to register. + public virtual void RegisterTokenProvider(string providerName, IUserTwoFactorTokenProvider provider) + { + ThrowIfDisposed(); + if (provider == null) + { + throw new ArgumentNullException(nameof(provider)); + } + _tokenProviders[providerName] = provider; + } + + /// + /// Gets a list of valid two factor token providers for the specified , + /// as an asynchronous operation. + /// + /// The user the whose two factor authentication providers will be returned. + /// + /// The that represents result of the asynchronous operation, a list of two + /// factor authentication providers for the specified user. + /// + public virtual async Task> GetValidTwoFactorProvidersAsync(TUser user) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var results = new List(); + foreach (var f in _tokenProviders) + { + if (await f.Value.CanGenerateTwoFactorTokenAsync(this, user)) + { + results.Add(f.Key); + } + } + return results; + } + + /// + /// Verifies the specified two factor authentication against the . + /// + /// The user the token is supposed to be for. + /// The provider which will verify the token. + /// The token to verify. + /// + /// The that represents result of the asynchronous operation, true if the token is valid, + /// otherwise false. + /// + public virtual async Task VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (!_tokenProviders.ContainsKey(tokenProvider)) + { + throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); + } + + // Make sure the token is valid + var result = await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user); + if (!result) + { + Logger.LogWarning(10, $"{nameof(VerifyTwoFactorTokenAsync)}() failed for user {await GetUserIdAsync(user)}."); + } + return result; + } + + /// + /// Gets a two factor authentication token for the specified . + /// + /// The user the token is for. + /// The provider which will generate the token. + /// + /// The that represents result of the asynchronous operation, a two factor authentication token + /// for the user. + /// + public virtual Task GenerateTwoFactorTokenAsync(TUser user, string tokenProvider) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (!_tokenProviders.ContainsKey(tokenProvider)) + { + throw new NotSupportedException(Resources.FormatNoTokenProvider(nameof(TUser), tokenProvider)); + } + + return _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user); + } + + /// + /// Returns a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be retrieved. + /// + /// The that represents the asynchronous operation, true if the specified + /// has two factor authentication enabled, otherwise false. + /// + public virtual async Task GetTwoFactorEnabledAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserTwoFactorStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetTwoFactorEnabledAsync(user, CancellationToken); + } + + /// + /// Sets a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// A flag indicating whether the specified has two factor authentication enabled. + /// + /// The that represents the asynchronous operation, the of the operation + /// + public virtual async Task SetTwoFactorEnabledAsync(TUser user, bool enabled) + { + ThrowIfDisposed(); + var store = GetUserTwoFactorStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await store.SetTwoFactorEnabledAsync(user, enabled, CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateUserAsync(user); + } + + /// + /// Returns a flag indicating whether the specified his locked out, + /// as an asynchronous operation. + /// + /// The user whose locked out status should be retrieved. + /// + /// The that represents the asynchronous operation, true if the specified + /// is locked out, otherwise false. + /// + public virtual async Task IsLockedOutAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) + { + return false; + } + var lockoutTime = await store.GetLockoutEndDateAsync(user, CancellationToken); + return lockoutTime >= DateTimeOffset.UtcNow; + } + + /// + /// Sets a flag indicating whether the specified is locked out, + /// as an asynchronous operation. + /// + /// The user whose locked out status should be set. + /// Flag indicating whether the user is locked out or not. + /// + /// The that represents the asynchronous operation, the of the operation + /// + public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await store.SetLockoutEnabledAsync(user, enabled, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Retrieves a flag indicating whether user lockout can enabled for the specified user. + /// + /// The user whose ability to be locked out should be returned. + /// + /// The that represents the asynchronous operation, true if a user can be locked out, otherwise false. + /// + public virtual async Task GetLockoutEnabledAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetLockoutEnabledAsync(user, CancellationToken); + } + + /// + /// Gets the last a user's last lockout expired, if any. + /// Any time in the past should be indicates a user is not locked out. + /// + /// The user whose lockout date should be retrieved. + /// + /// A that represents the lookup, a containing the last time a user's lockout expired, if any. + /// + public virtual async Task GetLockoutEndDateAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetLockoutEndDateAsync(user, CancellationToken); + } + + /// + /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user. + /// + /// The user whose lockout date should be set. + /// The after which the 's lockout should end. + /// The that represents the asynchronous operation, containing the of the operation. + public virtual async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) + { + Logger.LogWarning(11, "Lockout for user {userId} failed because lockout is not enabled for this user.", await GetUserIdAsync(user)); + return IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled()); + } + await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Increments the access failed count for the user as an asynchronous operation. + /// If the failed access account is greater than or equal to the configured maximum number of attempts, + /// the user will be locked out for the configured lockout time span. + /// + /// The user whose failed access count to increment. + /// The that represents the asynchronous operation, containing the of the operation. + public virtual async Task AccessFailedAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + // If this puts the user over the threshold for lockout, lock them out and reset the access failed count + var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken); + if (count < Options.Lockout.MaxFailedAccessAttempts) + { + return await UpdateUserAsync(user); + } + Logger.LogWarning(12, "User {userId} is locked out.", await GetUserIdAsync(user)); + await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), + CancellationToken); + await store.ResetAccessFailedCountAsync(user, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Resets the access failed count for the specified . + /// + /// The user whose failed access count should be reset. + /// The that represents the asynchronous operation, containing the of the operation. + public virtual async Task ResetAccessFailedCountAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (await GetAccessFailedCountAsync(user) == 0) + { + return IdentityResult.Success; + } + await store.ResetAccessFailedCountAsync(user, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Retrieves the current number of failed accesses for the given . + /// + /// The user whose access failed count should be retrieved for. + /// The that contains the result the asynchronous operation, the current failed access count + /// for the user. + public virtual async Task GetAccessFailedCountAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetUserLockoutStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return await store.GetAccessFailedCountAsync(user, CancellationToken); + } + + /// + /// Returns a list of users from the user store who have the specified . + /// + /// The claim to look for. + /// + /// A that represents the result of the asynchronous query, a list of s who + /// have the specified claim. + /// + public virtual Task> GetUsersForClaimAsync(Claim claim) + { + ThrowIfDisposed(); + var store = GetClaimStore(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + return store.GetUsersForClaimAsync(claim, CancellationToken); + } + + /// + /// Returns a list of users from the user store who are members of the specified . + /// + /// The name of the role whose users should be returned. + /// + /// A that represents the result of the asynchronous query, a list of s who + /// are members of the specified role. + /// + public virtual Task> GetUsersInRoleAsync(string roleName) + { + ThrowIfDisposed(); + var store = GetUserRoleStore(); + if (roleName == null) + { + throw new ArgumentNullException(nameof(roleName)); + } + + return store.GetUsersInRoleAsync(NormalizeKey(roleName), CancellationToken); + } + + /// + /// Returns an authentication token for a user. + /// + /// + /// The authentication scheme for the provider the token is associated with. + /// The name of the token. + /// The authentication token for a user + public virtual Task GetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) + { + ThrowIfDisposed(); + var store = GetAuthenticationTokenStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (loginProvider == null) + { + throw new ArgumentNullException(nameof(loginProvider)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + return store.GetTokenAsync(user, loginProvider, tokenName, CancellationToken); + } + + /// + /// Sets an authentication token for a user. + /// + /// + /// The authentication scheme for the provider the token is associated with. + /// The name of the token. + /// The value of the token. + /// Whether the user was successfully updated. + public virtual async Task SetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName, string tokenValue) + { + ThrowIfDisposed(); + var store = GetAuthenticationTokenStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (loginProvider == null) + { + throw new ArgumentNullException(nameof(loginProvider)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + // REVIEW: should updating any tokens affect the security stamp? + await store.SetTokenAsync(user, loginProvider, tokenName, tokenValue, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Remove an authentication token for a user. + /// + /// + /// The authentication scheme for the provider the token is associated with. + /// The name of the token. + /// Whether a token was removed. + public virtual async Task RemoveAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) + { + ThrowIfDisposed(); + var store = GetAuthenticationTokenStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (loginProvider == null) + { + throw new ArgumentNullException(nameof(loginProvider)); + } + if (tokenName == null) + { + throw new ArgumentNullException(nameof(tokenName)); + } + + await store.RemoveTokenAsync(user, loginProvider, tokenName, CancellationToken); + return await UpdateUserAsync(user); + } + + /// + /// Returns the authenticator key for the user. + /// + /// The user. + /// The authenticator key + public virtual Task GetAuthenticatorKeyAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetAuthenticatorKeyStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return store.GetAuthenticatorKeyAsync(user, CancellationToken); + } + + /// + /// Resets the authenticator key for the user. + /// + /// The user. + /// Whether the user was successfully updated. + public virtual async Task ResetAuthenticatorKeyAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetAuthenticatorKeyStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + await store.SetAuthenticatorKeyAsync(user, GenerateNewAuthenticatorKey(), CancellationToken); + await UpdateSecurityStampInternal(user); + return await UpdateAsync(user); + } + + /// + /// Generates a new base32 encoded 160-bit security secret (size of SHA1 hash). + /// + /// The new security secret. + public virtual string GenerateNewAuthenticatorKey() + => NewSecurityStamp(); + + /// + /// Generates recovery codes for the user, this invalidates any previous recovery codes for the user. + /// + /// The user to generate recovery codes for. + /// The number of codes to generate. + /// The new recovery codes for the user. Note: there may be less than number returned, as duplicates will be removed. + public virtual async Task> GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number) + { + ThrowIfDisposed(); + var store = GetRecoveryCodeStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var newCodes = new List(number); + for (var i = 0; i < number; i++) + { + newCodes.Add(CreateTwoFactorRecoveryCode()); + } + + await store.ReplaceCodesAsync(user, newCodes.Distinct(), CancellationToken); + var update = await UpdateAsync(user); + if (update.Succeeded) + { + return newCodes; + } + return null; + } + + /// + /// Generate a new recovery code. + /// + /// + protected virtual string CreateTwoFactorRecoveryCode() + => Guid.NewGuid().ToString().Substring(0, 8); + + /// + /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid + /// once, and will be invalid after use. + /// + /// The user who owns the recovery code. + /// The recovery code to use. + /// True if the recovery code was found for the user. + public virtual async Task RedeemTwoFactorRecoveryCodeAsync(TUser user, string code) + { + ThrowIfDisposed(); + var store = GetRecoveryCodeStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var success = await store.RedeemCodeAsync(user, code, CancellationToken); + if (success) + { + return await UpdateAsync(user); + } + return IdentityResult.Failed(ErrorDescriber.RecoveryCodeRedemptionFailed()); + } + + /// + /// Returns how many recovery code are still valid for a user. + /// + /// The user. + /// How many recovery code are still valid for a user. + public virtual Task CountRecoveryCodesAsync(TUser user) + { + ThrowIfDisposed(); + var store = GetRecoveryCodeStore(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return store.CountCodesAsync(user, CancellationToken); + } + + /// + /// Releases the unmanaged resources used by the role manager and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + Store.Dispose(); + _disposed = true; + } + } + + private IUserTwoFactorStore GetUserTwoFactorStore() + { + var cast = Store as IUserTwoFactorStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserTwoFactorStore); + } + return cast; + } + + private IUserLockoutStore GetUserLockoutStore() + { + var cast = Store as IUserLockoutStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserLockoutStore); + } + return cast; + } + + private IUserEmailStore GetEmailStore(bool throwOnFail = true) + { + var cast = Store as IUserEmailStore; + if (throwOnFail && cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserEmailStore); + } + return cast; + } + + private IUserPhoneNumberStore GetPhoneNumberStore() + { + var cast = Store as IUserPhoneNumberStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserPhoneNumberStore); + } + return cast; + } + + /// + /// Creates bytes to use as a security token from the user's security stamp. + /// + /// The user. + /// The security token bytes. + public virtual async Task CreateSecurityTokenAsync(TUser user) + { + return Encoding.Unicode.GetBytes(await GetSecurityStampAsync(user)); + } + + // Update the security stamp if the store supports it + private async Task UpdateSecurityStampInternal(TUser user) + { + if (SupportsUserSecurityStamp) + { + await GetSecurityStore().SetSecurityStampAsync(user, NewSecurityStamp(), CancellationToken); + } + } + + /// + /// Updates a user's password hash. + /// + /// The user. + /// The new password. + /// Whether to validate the password. + /// Whether the password has was successfully updated. + protected virtual Task UpdatePasswordHash(TUser user, string newPassword, bool validatePassword) + => UpdatePasswordHash(GetPasswordStore(), user, newPassword, validatePassword); + + private async Task UpdatePasswordHash(IUserPasswordStore passwordStore, + TUser user, string newPassword, bool validatePassword = true) + { + if (validatePassword) + { + var validate = await ValidatePasswordAsync(user, newPassword); + if (!validate.Succeeded) + { + return validate; + } + } + var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null; + await passwordStore.SetPasswordHashAsync(user, hash, CancellationToken); + await UpdateSecurityStampInternal(user); + return IdentityResult.Success; + } + + private IUserRoleStore GetUserRoleStore() + { + var cast = Store as IUserRoleStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserRoleStore); + } + return cast; + } + + private static string NewSecurityStamp() + { + byte[] bytes = new byte[20]; + _rng.GetBytes(bytes); + return Base32.ToBase32(bytes); + } + + // IUserLoginStore methods + private IUserLoginStore GetLoginStore() + { + var cast = Store as IUserLoginStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserLoginStore); + } + return cast; + } + + private IUserSecurityStampStore GetSecurityStore() + { + var cast = Store as IUserSecurityStampStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserSecurityStampStore); + } + return cast; + } + + private IUserClaimStore GetClaimStore() + { + var cast = Store as IUserClaimStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserClaimStore); + } + return cast; + } + + + /// + /// Generates the token purpose used to change email. + /// + /// The new email address. + /// The token purpose. + protected static string GetChangeEmailTokenPurpose(string newEmail) + { + return "ChangeEmail:" + newEmail; + } + + /// + /// Should return if validation is successful. This is + /// called before saving the user via Create or Update. + /// + /// The user + /// A representing whether validation was successful. + protected async Task ValidateUserAsync(TUser user) + { + if (SupportsUserSecurityStamp) + { + var stamp = await GetSecurityStampAsync(user); + if (stamp == null) + { + throw new InvalidOperationException(Resources.NullSecurityStamp); + } + } + var errors = new List(); + foreach (var v in UserValidators) + { + var result = await v.ValidateAsync(this, user); + if (!result.Succeeded) + { + errors.AddRange(result.Errors); + } + } + if (errors.Count > 0) + { + Logger.LogWarning(13, "User {userId} validation failed: {errors}.", await GetUserIdAsync(user), string.Join(";", errors.Select(e => e.Code))); + return IdentityResult.Failed(errors.ToArray()); + } + return IdentityResult.Success; + } + + /// + /// Should return if validation is successful. This is + /// called before updating the password hash. + /// + /// The user. + /// The password. + /// A representing whether validation was successful. + protected async Task ValidatePasswordAsync(TUser user, string password) + { + var errors = new List(); + foreach (var v in PasswordValidators) + { + var result = await v.ValidateAsync(this, user, password); + if (!result.Succeeded) + { + errors.AddRange(result.Errors); + } + } + if (errors.Count > 0) + { + Logger.LogWarning(14, "User {userId} password validation failed: {errors}.", await GetUserIdAsync(user), string.Join(";", errors.Select(e => e.Code))); + return IdentityResult.Failed(errors.ToArray()); + } + return IdentityResult.Success; + } + + /// + /// Called to update the user after validating and updating the normalized email/user name. + /// + /// The user. + /// Whether the operation was successful. + protected virtual async Task UpdateUserAsync(TUser user) + { + var result = await ValidateUserAsync(user); + if (!result.Succeeded) + { + return result; + } + await UpdateNormalizedUserNameAsync(user); + await UpdateNormalizedEmailAsync(user); + return await Store.UpdateAsync(user, CancellationToken); + } + + private IUserAuthenticatorKeyStore GetAuthenticatorKeyStore() + { + var cast = Store as IUserAuthenticatorKeyStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserAuthenticatorKeyStore); + } + return cast; + } + + private IUserTwoFactorRecoveryCodeStore GetRecoveryCodeStore() + { + var cast = Store as IUserTwoFactorRecoveryCodeStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserTwoFactorRecoveryCodeStore); + } + return cast; + } + + private IUserAuthenticationTokenStore GetAuthenticationTokenStore() + { + var cast = Store as IUserAuthenticationTokenStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserAuthenticationTokenStore); + } + return cast; + } + + private IUserPasswordStore GetPasswordStore() + { + var cast = Store as IUserPasswordStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIUserPasswordStore); + } + return cast; + } + + /// + /// Throws if this class has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + } +} diff --git a/src/Identity/src/Core/UserOptions.cs b/src/Identity/src/Core/UserOptions.cs new file mode 100644 index 0000000000..d0fc15862e --- /dev/null +++ b/src/Identity/src/Core/UserOptions.cs @@ -0,0 +1,27 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options for user validation. + /// + public class UserOptions + { + /// + /// Gets or sets the list of allowed characters in the username used to validate user names. Defaults to abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+ + /// + /// + /// The list of allowed characters in the username used to validate user names. + /// + public string AllowedUserNameCharacters { get; set; } = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; + + /// + /// Gets or sets a flag indicating whether the application requires unique emails for its users. Defaults to false. + /// + /// + /// True if the application requires each user to have their own, unique email, otherwise false. + /// + public bool RequireUniqueEmail { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Core/UserValidator.cs b/src/Identity/src/Core/UserValidator.cs new file mode 100644 index 0000000000..43d53962d2 --- /dev/null +++ b/src/Identity/src/Core/UserValidator.cs @@ -0,0 +1,103 @@ +// 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.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides validation services for user classes. + /// + /// The type encapsulating a user. + public class UserValidator : IUserValidator where TUser : class + { + /// + /// Creates a new instance of / + /// + /// The used to provider error messages. + public UserValidator(IdentityErrorDescriber errors = null) + { + Describer = errors ?? new IdentityErrorDescriber(); + } + + /// + /// Gets the used to provider error messages for the current . + /// + /// The used to provider error messages for the current . + public IdentityErrorDescriber Describer { get; private set; } + + /// + /// Validates the specified as an asynchronous operation. + /// + /// The that can be used to retrieve user properties. + /// The user to validate. + /// The that represents the asynchronous operation, containing the of the validation operation. + public virtual async Task ValidateAsync(UserManager manager, TUser user) + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var errors = new List(); + await ValidateUserName(manager, user, errors); + if (manager.Options.User.RequireUniqueEmail) + { + await ValidateEmail(manager, user, errors); + } + return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success; + } + + private async Task ValidateUserName(UserManager manager, TUser user, ICollection errors) + { + var userName = await manager.GetUserNameAsync(user); + if (string.IsNullOrWhiteSpace(userName)) + { + errors.Add(Describer.InvalidUserName(userName)); + } + else if (!string.IsNullOrEmpty(manager.Options.User.AllowedUserNameCharacters) && + userName.Any(c => !manager.Options.User.AllowedUserNameCharacters.Contains(c))) + { + errors.Add(Describer.InvalidUserName(userName)); + } + else + { + var owner = await manager.FindByNameAsync(userName); + if (owner != null && + !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) + { + errors.Add(Describer.DuplicateUserName(userName)); + } + } + } + + // make sure email is not empty, valid, and unique + private async Task ValidateEmail(UserManager manager, TUser user, List errors) + { + var email = await manager.GetEmailAsync(user); + if (string.IsNullOrWhiteSpace(email)) + { + errors.Add(Describer.InvalidEmail(email)); + return; + } + if (!new EmailAddressAttribute().IsValid(email)) + { + errors.Add(Describer.InvalidEmail(email)); + return; + } + var owner = await manager.FindByEmailAsync(email); + if (owner != null && + !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) + { + errors.Add(Describer.DuplicateEmail(email)); + } + } + } +} diff --git a/src/Identity/src/Core/baseline.netcore.json b/src/Identity/src/Core/baseline.netcore.json new file mode 100644 index 0000000000..e99ba1ccec --- /dev/null +++ b/src/Identity/src/Core/baseline.netcore.json @@ -0,0 +1,7305 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Identity.Core, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "System.Security.Claims.PrincipalExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "FindFirstValue", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "claimType", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddIdentityCore", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddIdentityCore", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RoleClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserNameClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserNameClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserIdClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserIdClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityStampClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityStampClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IPersonalDataProtector" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Unprotect", + "Parameters": [ + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPersonalDataProtector", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Protect", + "Parameters": [ + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPersonalDataProtector", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "keyRing", + "Type": "Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing" + }, + { + "Name": "protector", + "Type": "Microsoft.AspNetCore.Identity.ILookupProtector" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EmailTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Services", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TValidator", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddClaimsPrincipalFactory", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TFactory", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TDescriber", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddPasswordValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TValidator", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddUserStore", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TStore", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProvider", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + }, + { + "Name": "provider", + "Type": "System.Type" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUserManager", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoles", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddPersonalDataProtection", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProtector", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.ILookupProtector" + ] + }, + { + "ParameterName": "TKeyRing", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleStore", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TStore", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TRoleManager", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "user", + "Type": "System.Type" + }, + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "user", + "Type": "System.Type" + }, + { + "Name": "role", + "Type": "System.Type" + }, + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityError", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Code", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Code", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Description", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Description", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "DefaultError", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConcurrencyFailure", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordMismatch", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidToken", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RecoveryCodeRedemptionFailed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LoginAlreadyAssociated", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidUserName", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateUserName", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidRoleName", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateRoleName", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserAlreadyHasPassword", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserLockoutNotEnabled", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserAlreadyInRole", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserNotInRole", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordTooShort", + "Parameters": [ + { + "Name": "length", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresUniqueChars", + "Parameters": [ + { + "Name": "uniqueChars", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresNonAlphanumeric", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresDigit", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresLower", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresUpper", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ClaimsIdentity", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimsIdentity", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_User", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_User", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.UserOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Password", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Password", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.PasswordOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Lockout", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.LockoutOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Lockout", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.LockoutOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SignIn", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SignIn", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.SignInOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Tokens", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.TokenOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Tokens", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.TokenOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Stores", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.StoreOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Stores", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.StoreOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Succeeded", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Errors", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Failed", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityError[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Normalize", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ILookupProtector", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Protect", + "Parameters": [ + { + "Name": "keyId", + "Type": "System.String" + }, + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Unprotect", + "Parameters": [ + { + "Name": "keyId", + "Type": "System.String" + }, + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CurrentKeyId", + "Parameters": [], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Item", + "Parameters": [ + { + "Name": "keyId", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAllKeyIds", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "HashPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyHashedPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "hashedPassword", + "Type": "System.String" + }, + { + "Name": "providedPassword", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IPasswordValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IPersonalDataProtector", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Protect", + "Parameters": [ + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Unprotect", + "Parameters": [ + { + "Name": "data", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IProtectedUserStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "roleId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "email", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IncrementAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "passwordHash", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HasPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserSecurityStampStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "stamp", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetUserIdAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "userName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedUserName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "ReplaceCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "recoveryCodes", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RedeemCodeAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "code", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CountCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserTwoFactorStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.LockoutOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_AllowedForNewUsers", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AllowedForNewUsers", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_MaxFailedAccessAttempts", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaxFailedAccessAttempts", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_DefaultLockoutTimeSpan", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_DefaultLockoutTimeSpan", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasher", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IPasswordHasher" + ], + "Members": [ + { + "Kind": "Method", + "Name": "HashPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyHashedPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "hashedPassword", + "Type": "System.String" + }, + { + "Name": "providedPassword", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "IdentityV2", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "IdentityV3", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasherOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CompatibilityMode", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CompatibilityMode", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IterationCount", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IterationCount", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RequiredLength", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequiredLength", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequiredUniqueChars", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequiredUniqueChars", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireNonAlphanumeric", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireNonAlphanumeric", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireLowercase", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireLowercase", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireUppercase", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireUppercase", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireDigit", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireDigit", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IPasswordValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Describer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsDigit", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLower", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsUpper", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLetterOrDigit", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "Failed", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "Success", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + }, + { + "Kind": "Field", + "Name": "SuccessRehashNeeded", + "Parameters": [], + "GenericParameter": [], + "Literal": "2" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PersonalDataAttribute", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Attribute", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ProtectedPersonalDataAttribute", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.PersonalDataAttribute", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.RoleManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_CancellationToken", + "Parameters": [], + "ReturnType": "System.Threading.CancellationToken", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Store", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleValidators", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ErrorDescriber", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_KeyNormalizer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_KeyNormalizer", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsQueryableRoles", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsRoleClaims", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RoleExistsAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NormalizeKey", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "roleId", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [ + { + "Name": "disposing", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateRoleAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateRoleAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IRoleStore" + }, + { + "Name": "roleValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.RoleValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RequireConfirmedEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireConfirmedEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireConfirmedPhoneNumber", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireConfirmedPhoneNumber", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Succeeded", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IsLockedOut", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IsLockedOut", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IsNotAllowed", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IsNotAllowed", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequiresTwoFactor", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequiresTwoFactor", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Failed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LockedOut", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NotAllowed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorRequired", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.StoreOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_MaxLengthForKeys", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaxLengthForKeys", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProtectPersonalData", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtectPersonalData", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TokenOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProviderMap", + "Parameters": [], + "ReturnType": "System.Collections.Generic.Dictionary", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderMap", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.Dictionary" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EmailConfirmationTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EmailConfirmationTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PasswordResetTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PasswordResetTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ChangeEmailTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ChangeEmailTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ChangePhoneNumberTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ChangePhoneNumberTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AuthenticatorTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AuthenticatorTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultEmailProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultPhoneProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultAuthenticatorProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TokenProviderDescriptor", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProviderType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderInstance", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderInstance", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "type", + "Type": "System.Type" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ILookupNormalizer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Normalize", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userManager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RoleManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.RoleManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userManager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "roleManager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserLoginInfo", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_LoginProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LoginProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderKey", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderKey", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderDisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderDisplayName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_CancellationToken", + "Parameters": [], + "ReturnType": "System.Threading.CancellationToken", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Store", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Store", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IUserStore" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PasswordHasher", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PasswordHasher", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IPasswordHasher" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserValidators", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PasswordValidators", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_KeyNormalizer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_KeyNormalizer", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ErrorDescriber", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Options", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserAuthenticationTokens", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserAuthenticatorKey", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserTwoFactorRecoveryCodes", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserTwoFactor", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserPassword", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserSecurityStamp", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserRole", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserLogin", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserPhoneNumber", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserClaim", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserLockout", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsQueryableUsers", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserName", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserId", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserAsync", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateConcurrencyStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NormalizeKey", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserIdAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CheckPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HasPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "currentPassword", + "Type": "System.String" + }, + { + "Name": "newPassword", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemovePasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyPasswordAsync", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IUserPasswordStore" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GeneratePasswordResetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "newPassword", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roles", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roles", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateEmailConfirmationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfirmEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateChangeEmailTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "newEmail", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangeEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "newEmail", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateChangePhoneNumberTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyChangePhoneNumberTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyUserTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateUserTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "purpose", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RegisterTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + }, + { + "Name": "provider", + "Type": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetValidTwoFactorProvidersAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLockedOutAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AccessFailedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + }, + { + "Name": "tokenValue", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateNewAuthenticatorKey", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateNewTwoFactorRecoveryCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "number", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateTwoFactorRecoveryCode", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RedeemTwoFactorRecoveryCodeAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "code", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CountRecoveryCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [ + { + "Name": "disposing", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateSecurityTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdatePasswordHash", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "newPassword", + "Type": "System.String" + }, + { + "Name": "validatePassword", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetChangeEmailTokenPurpose", + "Parameters": [ + { + "Name": "newEmail", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateUserAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidatePasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateUserAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IUserStore" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "passwordHasher", + "Type": "Microsoft.AspNetCore.Identity.IPasswordHasher" + }, + { + "Name": "userValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "passwordValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "services", + "Type": "System.IServiceProvider" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ResetPasswordTokenPurpose", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"ResetPassword\"" + }, + { + "Kind": "Field", + "Name": "ChangePhoneNumberTokenPurpose", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"ChangePhoneNumber\"" + }, + { + "Kind": "Field", + "Name": "ConfirmEmailTokenPurpose", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"EmailConfirmation\"" + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_AllowedUserNameCharacters", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AllowedUserNameCharacters", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireUniqueEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireUniqueEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Describer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/Directory.Build.props b/src/Identity/src/Directory.Build.props new file mode 100644 index 0000000000..4b89a431e7 --- /dev/null +++ b/src/Identity/src/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Identity/src/EF/IdentityDbContext.cs b/src/Identity/src/EF/IdentityDbContext.cs new file mode 100644 index 0000000000..6bd4325a34 --- /dev/null +++ b/src/Identity/src/EF/IdentityDbContext.cs @@ -0,0 +1,155 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + /// + /// Base class for the Entity Framework database context used for identity. + /// + public class IdentityDbContext : IdentityDbContext + { + /// + /// Initializes a new instance of . + /// + /// The options to be used by a . + public IdentityDbContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityDbContext() { } + } + + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of the user objects. + public class IdentityDbContext : IdentityDbContext where TUser : IdentityUser + { + /// + /// Initializes a new instance of . + /// + /// The options to be used by a . + public IdentityDbContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityDbContext() { } + } + + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of user objects. + /// The type of role objects. + /// The type of the primary key for users and roles. + public class IdentityDbContext : IdentityDbContext, IdentityUserRole, IdentityUserLogin, IdentityRoleClaim, IdentityUserToken> + where TUser : IdentityUser + where TRole : IdentityRole + where TKey : IEquatable + { + /// + /// Initializes a new instance of the db context. + /// + /// The options to be used by a . + public IdentityDbContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityDbContext() { } + } + + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of user objects. + /// The type of role objects. + /// The type of the primary key for users and roles. + /// The type of the user claim object. + /// The type of the user role object. + /// The type of the user login object. + /// The type of the role claim object. + /// The type of the user token object. + public abstract class IdentityDbContext : IdentityUserContext + where TUser : IdentityUser + where TRole : IdentityRole + where TKey : IEquatable + where TUserClaim : IdentityUserClaim + where TUserRole : IdentityUserRole + where TUserLogin : IdentityUserLogin + where TRoleClaim : IdentityRoleClaim + where TUserToken : IdentityUserToken + { + /// + /// Initializes a new instance of the class. + /// + /// The options to be used by a . + public IdentityDbContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityDbContext() { } + + /// + /// Gets or sets the of User roles. + /// + public DbSet UserRoles { get; set; } + + /// + /// Gets or sets the of roles. + /// + public DbSet Roles { get; set; } + + /// + /// Gets or sets the of role claims. + /// + public DbSet RoleClaims { get; set; } + + /// + /// Configures the schema needed for the identity framework. + /// + /// + /// The builder being used to construct the model for this context. + /// + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + builder.Entity(b => + { + b.HasMany().WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); + }); + + builder.Entity(b => + { + b.HasKey(r => r.Id); + b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique(); + b.ToTable("AspNetRoles"); + b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken(); + + b.Property(u => u.Name).HasMaxLength(256); + b.Property(u => u.NormalizedName).HasMaxLength(256); + + b.HasMany().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired(); + b.HasMany().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired(); + }); + + builder.Entity(b => + { + b.HasKey(rc => rc.Id); + b.ToTable("AspNetRoleClaims"); + }); + + builder.Entity(b => + { + b.HasKey(r => new { r.UserId, r.RoleId }); + b.ToTable("AspNetUserRoles"); + }); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/EF/IdentityEntityFrameworkBuilderExtensions.cs b/src/Identity/src/EF/IdentityEntityFrameworkBuilderExtensions.cs new file mode 100644 index 0000000000..61474cf85b --- /dev/null +++ b/src/Identity/src/EF/IdentityEntityFrameworkBuilderExtensions.cs @@ -0,0 +1,113 @@ +// 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.Reflection; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Contains extension methods to for adding entity framework stores. + /// + public static class IdentityEntityFrameworkBuilderExtensions + { + /// + /// Adds an Entity Framework implementation of identity information stores. + /// + /// The Entity Framework database context to use. + /// The instance this method extends. + /// The instance this method extends. + public static IdentityBuilder AddEntityFrameworkStores(this IdentityBuilder builder) + where TContext : DbContext + { + AddStores(builder.Services, builder.UserType, builder.RoleType, typeof(TContext)); + return builder; + } + + private static void AddStores(IServiceCollection services, Type userType, Type roleType, Type contextType) + { + var identityUserType = FindGenericBaseType(userType, typeof(IdentityUser<>)); + if (identityUserType == null) + { + throw new InvalidOperationException(Resources.NotIdentityUser); + } + + var keyType = identityUserType.GenericTypeArguments[0]; + + if (roleType != null) + { + var identityRoleType = FindGenericBaseType(roleType, typeof(IdentityRole<>)); + if (identityRoleType == null) + { + throw new InvalidOperationException(Resources.NotIdentityRole); + } + + Type userStoreType = null; + Type roleStoreType = null; + var identityContext = FindGenericBaseType(contextType, typeof(IdentityDbContext<,,,,,,,>)); + if (identityContext == null) + { + // If its a custom DbContext, we can only add the default POCOs + userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType); + roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType); + } + else + { + userStoreType = typeof(UserStore<,,,,,,,,>).MakeGenericType(userType, roleType, contextType, + identityContext.GenericTypeArguments[2], + identityContext.GenericTypeArguments[3], + identityContext.GenericTypeArguments[4], + identityContext.GenericTypeArguments[5], + identityContext.GenericTypeArguments[7], + identityContext.GenericTypeArguments[6]); + roleStoreType = typeof(RoleStore<,,,,>).MakeGenericType(roleType, contextType, + identityContext.GenericTypeArguments[2], + identityContext.GenericTypeArguments[4], + identityContext.GenericTypeArguments[6]); + } + services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), userStoreType); + services.TryAddScoped(typeof(IRoleStore<>).MakeGenericType(roleType), roleStoreType); + } + else + { // No Roles + Type userStoreType = null; + var identityContext = FindGenericBaseType(contextType, typeof(IdentityUserContext<,,,,>)); + if (identityContext == null) + { + // If its a custom DbContext, we can only add the default POCOs + userStoreType = typeof(UserOnlyStore<,,>).MakeGenericType(userType, contextType, keyType); + } + else + { + userStoreType = typeof(UserOnlyStore<,,,,,>).MakeGenericType(userType, contextType, + identityContext.GenericTypeArguments[1], + identityContext.GenericTypeArguments[2], + identityContext.GenericTypeArguments[3], + identityContext.GenericTypeArguments[4]); + } + services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), userStoreType); + } + + } + + private static TypeInfo FindGenericBaseType(Type currentType, Type genericBaseType) + { + var type = currentType; + while (type != null) + { + var typeInfo = type.GetTypeInfo(); + var genericType = type.IsGenericType ? type.GetGenericTypeDefinition() : null; + if (genericType != null && genericType == genericBaseType) + { + return typeInfo; + } + type = type.BaseType; + } + return null; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/EF/IdentityUserContext.cs b/src/Identity/src/EF/IdentityUserContext.cs new file mode 100644 index 0000000000..73d7664a05 --- /dev/null +++ b/src/Identity/src/EF/IdentityUserContext.cs @@ -0,0 +1,204 @@ +// 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 Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of the user objects. + public class IdentityUserContext : IdentityUserContext where TUser : IdentityUser + { + /// + /// Initializes a new instance of . + /// + /// The options to be used by a . + public IdentityUserContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityUserContext() { } + } + + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of user objects. + /// The type of the primary key for users and roles. + public class IdentityUserContext : IdentityUserContext, IdentityUserLogin, IdentityUserToken> + where TUser : IdentityUser + where TKey : IEquatable + { + /// + /// Initializes a new instance of the db context. + /// + /// The options to be used by a . + public IdentityUserContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityUserContext() { } + } + + /// + /// Base class for the Entity Framework database context used for identity. + /// + /// The type of user objects. + /// The type of the primary key for users and roles. + /// The type of the user claim object. + /// The type of the user login object. + /// The type of the user token object. + public abstract class IdentityUserContext : DbContext + where TUser : IdentityUser + where TKey : IEquatable + where TUserClaim : IdentityUserClaim + where TUserLogin : IdentityUserLogin + where TUserToken : IdentityUserToken + { + /// + /// Initializes a new instance of the class. + /// + /// The options to be used by a . + public IdentityUserContext(DbContextOptions options) : base(options) { } + + /// + /// Initializes a new instance of the class. + /// + protected IdentityUserContext() { } + + /// + /// Gets or sets the of Users. + /// + public DbSet Users { get; set; } + + /// + /// Gets or sets the of User claims. + /// + public DbSet UserClaims { get; set; } + + /// + /// Gets or sets the of User logins. + /// + public DbSet UserLogins { get; set; } + + /// + /// Gets or sets the of User tokens. + /// + public DbSet UserTokens { get; set; } + + private StoreOptions GetStoreOptions() => this.GetService() + .Extensions.OfType() + .FirstOrDefault()?.ApplicationServiceProvider + ?.GetService>() + ?.Value?.Stores; + + private class PersonalDataConverter : ValueConverter + { + public PersonalDataConverter(IPersonalDataProtector protector) : base(s => protector.Protect(s), s => protector.Unprotect(s), default) + { } + } + + /// + /// Configures the schema needed for the identity framework. + /// + /// + /// The builder being used to construct the model for this context. + /// + protected override void OnModelCreating(ModelBuilder builder) + { + var storeOptions = GetStoreOptions(); + var maxKeyLength = storeOptions?.MaxLengthForKeys ?? 0; + var encryptPersonalData = storeOptions?.ProtectPersonalData ?? false; + PersonalDataConverter converter = null; + + builder.Entity(b => + { + b.HasKey(u => u.Id); + b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique(); + b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex"); + b.ToTable("AspNetUsers"); + b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken(); + + b.Property(u => u.UserName).HasMaxLength(256); + b.Property(u => u.NormalizedUserName).HasMaxLength(256); + b.Property(u => u.Email).HasMaxLength(256); + b.Property(u => u.NormalizedEmail).HasMaxLength(256); + + if (encryptPersonalData) + { + converter = new PersonalDataConverter(this.GetService()); + var personalDataProps = typeof(TUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute))); + foreach (var p in personalDataProps) + { + if (p.PropertyType != typeof(string)) + { + throw new InvalidOperationException(Resources.CanOnlyProtectStrings); + } + b.Property(typeof(string), p.Name).HasConversion(converter); + } + } + + b.HasMany().WithOne().HasForeignKey(uc => uc.UserId).IsRequired(); + b.HasMany().WithOne().HasForeignKey(ul => ul.UserId).IsRequired(); + b.HasMany().WithOne().HasForeignKey(ut => ut.UserId).IsRequired(); + }); + + builder.Entity(b => + { + b.HasKey(uc => uc.Id); + b.ToTable("AspNetUserClaims"); + }); + + builder.Entity(b => + { + b.HasKey(l => new { l.LoginProvider, l.ProviderKey }); + + if (maxKeyLength > 0) + { + b.Property(l => l.LoginProvider).HasMaxLength(maxKeyLength); + b.Property(l => l.ProviderKey).HasMaxLength(maxKeyLength); + } + + b.ToTable("AspNetUserLogins"); + }); + + builder.Entity(b => + { + b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name }); + + if (maxKeyLength > 0) + { + b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength); + b.Property(t => t.Name).HasMaxLength(maxKeyLength); + } + + if (encryptPersonalData) + { + var tokenProps = typeof(TUserToken).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(ProtectedPersonalDataAttribute))); + foreach (var p in tokenProps) + { + if (p.PropertyType != typeof(string)) + { + throw new InvalidOperationException(Resources.CanOnlyProtectStrings); + } + b.Property(typeof(string), p.Name).HasConversion(converter); + } + } + + b.ToTable("AspNetUserTokens"); + }); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/EF/Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj b/src/Identity/src/EF/Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..79339b9c1e --- /dev/null +++ b/src/Identity/src/EF/Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj @@ -0,0 +1,19 @@ + + + + ASP.NET Core Identity provider that uses Entity Framework Core. + netstandard2.0 + true + aspnetcore;entityframeworkcore;identity;membership + + + + + + + + + + + + diff --git a/src/Identity/src/EF/Properties/Resources.Designer.cs b/src/Identity/src/EF/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..7cc882ef40 --- /dev/null +++ b/src/Identity/src/EF/Properties/Resources.Designer.cs @@ -0,0 +1,100 @@ +// +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNetCore.Identity.EntityFrameworkCore.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// [ProtectedPersonalData] only works strings by default. + /// + internal static string CanOnlyProtectStrings + { + get => GetString("CanOnlyProtectStrings"); + } + + /// + /// [ProtectedPersonalData] only works strings by default. + /// + internal static string FormatCanOnlyProtectStrings() + => GetString("CanOnlyProtectStrings"); + + /// + /// AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey>. + /// + internal static string NotIdentityRole + { + get => GetString("NotIdentityRole"); + } + + /// + /// AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey>. + /// + internal static string FormatNotIdentityRole() + => GetString("NotIdentityRole"); + + /// + /// AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey>. + /// + internal static string NotIdentityUser + { + get => GetString("NotIdentityUser"); + } + + /// + /// AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey>. + /// + internal static string FormatNotIdentityUser() + => GetString("NotIdentityUser"); + + /// + /// Role {0} does not exist. + /// + internal static string RoleNotFound + { + get => GetString("RoleNotFound"); + } + + /// + /// Role {0} does not exist. + /// + internal static string FormatRoleNotFound(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0); + + /// + /// Value cannot be null or empty. + /// + internal static string ValueCannotBeNullOrEmpty + { + get => GetString("ValueCannotBeNullOrEmpty"); + } + + /// + /// Value cannot be null or empty. + /// + internal static string FormatValueCannotBeNullOrEmpty() + => GetString("ValueCannotBeNullOrEmpty"); + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Identity/src/EF/Resources.resx b/src/Identity/src/EF/Resources.resx new file mode 100644 index 0000000000..1f85b1395e --- /dev/null +++ b/src/Identity/src/EF/Resources.resx @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + [ProtectedPersonalData] only works strings by default. + error when attribute is used on a non string property. + + + AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey>. + error when the role does not derive from IdentityRole + + + AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey>. + error when the user does not derive from IdentityUser + + + Role {0} does not exist. + error when a role does not exist + + + Value cannot be null or empty. + error when something cannot be null or empty + + \ No newline at end of file diff --git a/src/Identity/src/EF/RoleStore.cs b/src/Identity/src/EF/RoleStore.cs new file mode 100644 index 0000000000..04745e024c --- /dev/null +++ b/src/Identity/src/EF/RoleStore.cs @@ -0,0 +1,446 @@ +// 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.ComponentModel; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + /// + /// Creates a new instance of a persistence store for roles. + /// + /// The type of the class representing a role + public class RoleStore : RoleStore + where TRole : IdentityRole + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public RoleStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Creates a new instance of a persistence store for roles. + /// + /// The type of the class representing a role. + /// The type of the data context class used to access the store. + public class RoleStore : RoleStore + where TRole : IdentityRole + where TContext : DbContext + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Creates a new instance of a persistence store for roles. + /// + /// The type of the class representing a role. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + public class RoleStore : RoleStore, IdentityRoleClaim>, + IQueryableRoleStore, + IRoleClaimStore + where TRole : IdentityRole + where TKey : IEquatable + where TContext : DbContext + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Creates a new instance of a persistence store for roles. + /// + /// The type of the class representing a role. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + /// The type of the class representing a user role. + /// The type of the class representing a role claim. + public class RoleStore : + IQueryableRoleStore, + IRoleClaimStore + where TRole : IdentityRole + where TKey : IEquatable + where TContext : DbContext + where TUserRole : IdentityUserRole, new() + where TRoleClaim : IdentityRoleClaim, new() + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public RoleStore(TContext context, IdentityErrorDescriber describer = null) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + Context = context; + ErrorDescriber = describer ?? new IdentityErrorDescriber(); + } + + private bool _disposed; + + + /// + /// Gets the database context for this store. + /// + public TContext Context { get; private set; } + + /// + /// Gets or sets the for any error that occurred with the current operation. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called. + /// + /// + /// True if changes should be automatically persisted, otherwise false. + /// + public bool AutoSaveChanges { get; set; } = true; + + /// Saves the current store. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + private async Task SaveChanges(CancellationToken cancellationToken) + { + if (AutoSaveChanges) + { + await Context.SaveChangesAsync(cancellationToken); + } + } + + /// + /// Creates a new role in a store as an asynchronous operation. + /// + /// The role to create in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public async virtual Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + Context.Add(role); + await SaveChanges(cancellationToken); + return IdentityResult.Success; + } + + /// + /// Updates a role in a store as an asynchronous operation. + /// + /// The role to update in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public async virtual Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + Context.Attach(role); + role.ConcurrencyStamp = Guid.NewGuid().ToString(); + Context.Update(role); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Deletes a role from the store as an asynchronous operation. + /// + /// The role to delete from the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public async virtual Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + Context.Remove(role); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Gets the ID for a role from the store as an asynchronous operation. + /// + /// The role whose ID should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the ID of the role. + public virtual Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(ConvertIdToString(role.Id)); + } + + /// + /// Gets the name of a role from the store as an asynchronous operation. + /// + /// The role whose name should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public virtual Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(role.Name); + } + + /// + /// Sets the name of a role in the store as an asynchronous operation. + /// + /// The role whose name should be set. + /// The name of the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + role.Name = roleName; + return Task.CompletedTask; + } + + /// + /// Converts the provided to a strongly typed key object. + /// + /// The id to convert. + /// An instance of representing the provided . + public virtual TKey ConvertIdFromString(string id) + { + if (id == null) + { + return default(TKey); + } + return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id); + } + + /// + /// Converts the provided to its string representation. + /// + /// The id to convert. + /// An representation of the provided . + public virtual string ConvertIdToString(TKey id) + { + if (id.Equals(default(TKey))) + { + return null; + } + return id.ToString(); + } + + /// + /// Finds the role who has the specified ID as an asynchronous operation. + /// + /// The role ID to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public virtual Task FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var roleId = ConvertIdFromString(id); + return Roles.FirstOrDefaultAsync(u => u.Id.Equals(roleId), cancellationToken); + } + + /// + /// Finds the role who has the specified normalized name as an asynchronous operation. + /// + /// The normalized role name to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public virtual Task FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName, cancellationToken); + } + + /// + /// Get a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public virtual Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(role.NormalizedName); + } + + /// + /// Set a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be set. + /// The normalized name to set + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + role.NormalizedName = normalizedName; + return Task.CompletedTask; + } + + /// + /// Throws if this class has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + /// + /// Dispose the stores + /// + public void Dispose() => _disposed = true; + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The role whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a role. + public async virtual Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + + return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken); + } + + /// + /// Adds the given to the specified . + /// + /// The role to add the claim to. + /// The claim to add to the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + + RoleClaims.Add(CreateRoleClaim(role, claim)); + return Task.FromResult(false); + } + + /// + /// Removes the given from the specified . + /// + /// The role to remove the claim from. + /// The claim to remove from the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async virtual Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + var claims = await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id) && rc.ClaimValue == claim.Value && rc.ClaimType == claim.Type).ToListAsync(cancellationToken); + foreach (var c in claims) + { + RoleClaims.Remove(c); + } + } + + /// + /// A navigation property for the roles the store contains. + /// + public virtual IQueryable Roles => Context.Set(); + + private DbSet RoleClaims { get { return Context.Set(); } } + + /// + /// Creates an entity representing a role claim. + /// + /// The associated role. + /// The associated claim. + /// The role claim entity. + protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim) + => new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }; + } +} diff --git a/src/Identity/src/EF/UserOnlyStore.cs b/src/Identity/src/EF/UserOnlyStore.cs new file mode 100644 index 0000000000..614d4fbf6d --- /dev/null +++ b/src/Identity/src/EF/UserOnlyStore.cs @@ -0,0 +1,568 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + /// + /// Creates a new instance of a persistence store for the specified user type. + /// + /// The type representing a user. + public class UserOnlyStore : UserOnlyStore where TUser : IdentityUser, new() + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserOnlyStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type of the data context class used to access the store. + public class UserOnlyStore : UserOnlyStore + where TUser : IdentityUser + where TContext : DbContext + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + public class UserOnlyStore : UserOnlyStore, IdentityUserLogin, IdentityUserToken> + where TUser : IdentityUser + where TContext : DbContext + where TKey : IEquatable + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + /// The type representing a claim. + /// The type representing a user external login. + /// The type representing a user token. + public class UserOnlyStore : + UserStoreBase, + IUserLoginStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore, + IQueryableUserStore, + IUserTwoFactorStore, + IUserAuthenticationTokenStore, + IUserAuthenticatorKeyStore, + IUserTwoFactorRecoveryCodeStore, + IProtectedUserStore + where TUser : IdentityUser + where TContext : DbContext + where TKey : IEquatable + where TUserClaim : IdentityUserClaim, new() + where TUserLogin : IdentityUserLogin, new() + where TUserToken : IdentityUserToken, new() + { + /// + /// Creates a new instance of the store. + /// + /// The context used to access the store. + /// The used to describe store errors. + public UserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber()) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + Context = context; + } + + /// + /// Gets the database context for this store. + /// + public TContext Context { get; private set; } + + /// + /// DbSet of users. + /// + protected DbSet UsersSet { get { return Context.Set(); } } + + /// + /// DbSet of user claims. + /// + protected DbSet UserClaims { get { return Context.Set(); } } + + /// + /// DbSet of user logins. + /// + protected DbSet UserLogins { get { return Context.Set(); } } + + /// + /// DbSet of user tokens. + /// + protected DbSet UserTokens { get { return Context.Set(); } } + + /// + /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called. + /// + /// + /// True if changes should be automatically persisted, otherwise false. + /// + public bool AutoSaveChanges { get; set; } = true; + + /// Saves the current store. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + protected Task SaveChanges(CancellationToken cancellationToken) + { + return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask; + } + + /// + /// Creates the specified in the user store. + /// + /// The user to create. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the creation operation. + public async override Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + Context.Add(user); + await SaveChanges(cancellationToken); + return IdentityResult.Success; + } + + /// + /// Updates the specified in the user store. + /// + /// The user to update. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public async override Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + Context.Attach(user); + user.ConcurrencyStamp = Guid.NewGuid().ToString(); + Context.Update(user); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Deletes the specified from the user store. + /// + /// The user to delete. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public async override Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + Context.Remove(user); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public override Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var id = ConvertIdFromString(userId); + return UsersSet.FindAsync(new object[] { id }, cancellationToken); + } + + /// + /// Finds and returns a user, if any, who has the specified normalized user name. + /// + /// The normalized user name to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public override Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken); + } + + /// + /// A navigation property for the users the store contains. + /// + public override IQueryable Users + { + get { return UsersSet; } + } + + /// + /// Return a user with the matching userId if it exists. + /// + /// The user's id. + /// The used to propagate notifications that the operation should be canceled. + /// The user if it exists. + protected override Task FindUserAsync(TKey userId, CancellationToken cancellationToken) + { + return Users.SingleOrDefaultAsync(u => u.Id.Equals(userId), cancellationToken); + } + + /// + /// Return a user login with the matching userId, provider, providerKey if it exists. + /// + /// The user's id. + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected override Task FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken) + { + return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(userId) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken); + } + + /// + /// Return a user login with provider, providerKey if it exists. + /// + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected override Task FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) + { + return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken); + } + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The user whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a user. + public async override Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken); + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the claim to. + /// The claim to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + foreach (var claim in claims) + { + UserClaims.Add(CreateUserClaim(user, claim)); + } + return Task.FromResult(false); + } + + /// + /// Replaces the on the specified , with the . + /// + /// The user to replace the claim on. + /// The claim replace. + /// The new claim replacing the . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + if (newClaim == null) + { + throw new ArgumentNullException(nameof(newClaim)); + } + + var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken); + foreach (var matchedClaim in matchedClaims) + { + matchedClaim.ClaimValue = newClaim.Value; + matchedClaim.ClaimType = newClaim.Type; + } + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the claims from. + /// The claim to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + foreach (var claim in claims) + { + var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken); + foreach (var c in matchedClaims) + { + UserClaims.Remove(c); + } + } + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the login to. + /// The login to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override Task AddLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (login == null) + { + throw new ArgumentNullException(nameof(login)); + } + UserLogins.Add(CreateUserLogin(user, login)); + return Task.FromResult(false); + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the login from. + /// The login to remove from the user. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var entry = await FindUserLoginAsync(user.Id, loginProvider, providerKey, cancellationToken); + if (entry != null) + { + UserLogins.Remove(entry); + } + } + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + public async override Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var userId = user.Id; + return await UserLogins.Where(l => l.UserId.Equals(userId)) + .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken); + } + + /// + /// Retrieves the user associated with the specified login provider and login provider key. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + public async override Task FindByLoginAsync(string loginProvider, string providerKey, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken); + if (userLogin != null) + { + return await FindUserAsync(userLogin.UserId, cancellationToken); + } + return null; + } + + /// + /// Gets the user, if any, associated with the specified, normalized email address. + /// + /// The normalized email address to return the user for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address. + /// + public override Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken); + } + + /// + /// Retrieves all users with the specified claim. + /// + /// The claim whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that contain the specified claim. + /// + public async override Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + + var query = from userclaims in UserClaims + join user in Users on userclaims.UserId equals user.Id + where userclaims.ClaimValue == claim.Value + && userclaims.ClaimType == claim.Type + select user; + + return await query.ToListAsync(cancellationToken); + } + + /// + /// Find a user token if it exists. + /// + /// The token owner. + /// The login provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The user token if it exists. + protected override Task FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + => UserTokens.FindAsync(new object[] { user.Id, loginProvider, name }, cancellationToken); + + /// + /// Add a new user token. + /// + /// The token to be added. + /// + protected override Task AddUserTokenAsync(TUserToken token) + { + UserTokens.Add(token); + return Task.CompletedTask; + } + + + /// + /// Remove a new user token. + /// + /// The token to be removed. + /// + protected override Task RemoveUserTokenAsync(TUserToken token) + { + UserTokens.Remove(token); + return Task.CompletedTask; + } + } +} diff --git a/src/Identity/src/EF/UserStore.cs b/src/Identity/src/EF/UserStore.cs new file mode 100644 index 0000000000..453cb6efec --- /dev/null +++ b/src/Identity/src/EF/UserStore.cs @@ -0,0 +1,732 @@ +// 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.Globalization; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore +{ + /// + /// Represents a new instance of a persistence store for users, using the default implementation + /// of with a string as a primary key. + /// + public class UserStore : UserStore> + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Creates a new instance of a persistence store for the specified user type. + /// + /// The type representing a user. + public class UserStore : UserStore + where TUser : IdentityUser, new() + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type representing a role. + /// The type of the data context class used to access the store. + public class UserStore : UserStore + where TUser : IdentityUser + where TRole : IdentityRole + where TContext : DbContext + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type representing a role. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + public class UserStore : UserStore, IdentityUserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim> + where TUser : IdentityUser + where TRole : IdentityRole + where TContext : DbContext + where TKey : IEquatable + { + /// + /// Constructs a new instance of . + /// + /// The . + /// The . + public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type representing a role. + /// The type of the data context class used to access the store. + /// The type of the primary key for a role. + /// The type representing a claim. + /// The type representing a user role. + /// The type representing a user external login. + /// The type representing a user token. + /// The type representing a role claim. + public class UserStore : + UserStoreBase, + IProtectedUserStore + where TUser : IdentityUser + where TRole : IdentityRole + where TContext : DbContext + where TKey : IEquatable + where TUserClaim : IdentityUserClaim, new() + where TUserRole : IdentityUserRole, new() + where TUserLogin : IdentityUserLogin, new() + where TUserToken : IdentityUserToken, new() + where TRoleClaim : IdentityRoleClaim, new() + { + /// + /// Creates a new instance of the store. + /// + /// The context used to access the store. + /// The used to describe store errors. + public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber()) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + Context = context; + } + + /// + /// Gets the database context for this store. + /// + public TContext Context { get; private set; } + + private DbSet UsersSet { get { return Context.Set(); } } + private DbSet Roles { get { return Context.Set(); } } + private DbSet UserClaims { get { return Context.Set(); } } + private DbSet UserRoles { get { return Context.Set(); } } + private DbSet UserLogins { get { return Context.Set(); } } + private DbSet UserTokens { get { return Context.Set(); } } + + /// + /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called. + /// + /// + /// True if changes should be automatically persisted, otherwise false. + /// + public bool AutoSaveChanges { get; set; } = true; + + /// Saves the current store. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + protected Task SaveChanges(CancellationToken cancellationToken) + { + return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask; + } + + /// + /// Creates the specified in the user store. + /// + /// The user to create. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the creation operation. + public async override Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + Context.Add(user); + await SaveChanges(cancellationToken); + return IdentityResult.Success; + } + + /// + /// Updates the specified in the user store. + /// + /// The user to update. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public async override Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + Context.Attach(user); + user.ConcurrencyStamp = Guid.NewGuid().ToString(); + Context.Update(user); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Deletes the specified from the user store. + /// + /// The user to delete. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public async override Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + Context.Remove(user); + try + { + await SaveChanges(cancellationToken); + } + catch (DbUpdateConcurrencyException) + { + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + return IdentityResult.Success; + } + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public override Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var id = ConvertIdFromString(userId); + return UsersSet.FindAsync(new object[] { id }, cancellationToken); + } + + /// + /// Finds and returns a user, if any, who has the specified normalized user name. + /// + /// The normalized user name to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public override Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken); + } + + /// + /// A navigation property for the users the store contains. + /// + public override IQueryable Users + { + get { return UsersSet; } + } + + /// + /// Return a role with the normalized name if it exists. + /// + /// The normalized role name. + /// The used to propagate notifications that the operation should be canceled. + /// The role if it exists. + protected override Task FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken) + { + return Roles.SingleOrDefaultAsync(r => r.NormalizedName == normalizedRoleName, cancellationToken); + } + + /// + /// Return a user role for the userId and roleId if it exists. + /// + /// The user's id. + /// The role's id. + /// The used to propagate notifications that the operation should be canceled. + /// The user role if it exists. + protected override Task FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken) + { + return UserRoles.FindAsync(new object[] { userId, roleId }, cancellationToken); + } + + /// + /// Return a user with the matching userId if it exists. + /// + /// The user's id. + /// The used to propagate notifications that the operation should be canceled. + /// The user if it exists. + protected override Task FindUserAsync(TKey userId, CancellationToken cancellationToken) + { + return Users.SingleOrDefaultAsync(u => u.Id.Equals(userId), cancellationToken); + } + + /// + /// Return a user login with the matching userId, provider, providerKey if it exists. + /// + /// The user's id. + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected override Task FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken) + { + return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(userId) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken); + } + + /// + /// Return a user login with provider, providerKey if it exists. + /// + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected override Task FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) + { + return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken); + } + + + /// + /// Adds the given to the specified . + /// + /// The user to add the role to. + /// The role to add. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (string.IsNullOrWhiteSpace(normalizedRoleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName)); + } + var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken); + if (roleEntity == null) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, normalizedRoleName)); + } + UserRoles.Add(CreateUserRole(user, roleEntity)); + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the role from. + /// The role to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (string.IsNullOrWhiteSpace(normalizedRoleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName)); + } + var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken); + if (roleEntity != null) + { + var userRole = await FindUserRoleAsync(user.Id, roleEntity.Id, cancellationToken); + if (userRole != null) + { + UserRoles.Remove(userRole); + } + } + } + + /// + /// Retrieves the roles the specified is a member of. + /// + /// The user whose roles should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the roles the user is a member of. + public override async Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var userId = user.Id; + var query = from userRole in UserRoles + join role in Roles on userRole.RoleId equals role.Id + where userRole.UserId.Equals(userId) + select role.Name; + return await query.ToListAsync(cancellationToken); + } + + /// + /// Returns a flag indicating if the specified user is a member of the give . + /// + /// The user whose role membership should be checked. + /// The role to check membership of + /// The used to propagate notifications that the operation should be canceled. + /// A containing a flag indicating if the specified user is a member of the given group. If the + /// user is a member of the group the returned value with be true, otherwise it will be false. + public override async Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (string.IsNullOrWhiteSpace(normalizedRoleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName)); + } + var role = await FindRoleAsync(normalizedRoleName, cancellationToken); + if (role != null) + { + var userRole = await FindUserRoleAsync(user.Id, role.Id, cancellationToken); + return userRole != null; + } + return false; + } + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The user whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a user. + public async override Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken); + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the claim to. + /// The claim to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + foreach (var claim in claims) + { + UserClaims.Add(CreateUserClaim(user, claim)); + } + return Task.FromResult(false); + } + + /// + /// Replaces the on the specified , with the . + /// + /// The user to replace the claim on. + /// The claim replace. + /// The new claim replacing the . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + if (newClaim == null) + { + throw new ArgumentNullException(nameof(newClaim)); + } + + var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken); + foreach (var matchedClaim in matchedClaims) + { + matchedClaim.ClaimValue = newClaim.Value; + matchedClaim.ClaimType = newClaim.Type; + } + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the claims from. + /// The claim to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async override Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + foreach (var claim in claims) + { + var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken); + foreach (var c in matchedClaims) + { + UserClaims.Remove(c); + } + } + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the login to. + /// The login to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override Task AddLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (login == null) + { + throw new ArgumentNullException(nameof(login)); + } + UserLogins.Add(CreateUserLogin(user, login)); + return Task.FromResult(false); + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the login from. + /// The login to remove from the user. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var entry = await FindUserLoginAsync(user.Id, loginProvider, providerKey, cancellationToken); + if (entry != null) + { + UserLogins.Remove(entry); + } + } + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + public async override Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var userId = user.Id; + return await UserLogins.Where(l => l.UserId.Equals(userId)) + .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken); + } + + /// + /// Retrieves the user associated with the specified login provider and login provider key. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + public async override Task FindByLoginAsync(string loginProvider, string providerKey, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken); + if (userLogin != null) + { + return await FindUserAsync(userLogin.UserId, cancellationToken); + } + return null; + } + + /// + /// Gets the user, if any, associated with the specified, normalized email address. + /// + /// The normalized email address to return the user for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address. + /// + public override Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken); + } + + /// + /// Retrieves all users with the specified claim. + /// + /// The claim whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that contain the specified claim. + /// + public async override Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (claim == null) + { + throw new ArgumentNullException(nameof(claim)); + } + + var query = from userclaims in UserClaims + join user in Users on userclaims.UserId equals user.Id + where userclaims.ClaimValue == claim.Value + && userclaims.ClaimType == claim.Type + select user; + + return await query.ToListAsync(cancellationToken); + } + + /// + /// Retrieves all users in the specified role. + /// + /// The role whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that are in the specified role. + /// + public async override Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (string.IsNullOrEmpty(normalizedRoleName)) + { + throw new ArgumentNullException(nameof(normalizedRoleName)); + } + + var role = await FindRoleAsync(normalizedRoleName, cancellationToken); + + if (role != null) + { + var query = from userrole in UserRoles + join user in Users on userrole.UserId equals user.Id + where userrole.RoleId.Equals(role.Id) + select user; + + return await query.ToListAsync(cancellationToken); + } + return new List(); + } + + /// + /// Find a user token if it exists. + /// + /// The token owner. + /// The login provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The user token if it exists. + protected override Task FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + => UserTokens.FindAsync(new object[] { user.Id, loginProvider, name }, cancellationToken); + + /// + /// Add a new user token. + /// + /// The token to be added. + /// + protected override Task AddUserTokenAsync(TUserToken token) + { + UserTokens.Add(token); + return Task.CompletedTask; + } + + + /// + /// Remove a new user token. + /// + /// The token to be removed. + /// + protected override Task RemoveUserTokenAsync(TUserToken token) + { + UserTokens.Remove(token); + return Task.CompletedTask; + } + } +} diff --git a/src/Identity/src/EF/baseline.netcore.json b/src/Identity/src/EF/baseline.netcore.json new file mode 100644 index 0000000000..c20ee5296f --- /dev/null +++ b/src/Identity/src/EF/baseline.netcore.json @@ -0,0 +1,2873 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Identity.EntityFrameworkCore, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddEntityFrameworkStores", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Identity.IdentityBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TContext", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext, Microsoft.AspNetCore.Identity.IdentityUserRole, Microsoft.AspNetCore.Identity.IdentityUserLogin, Microsoft.AspNetCore.Identity.IdentityRoleClaim, Microsoft.AspNetCore.Identity.IdentityUserToken>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserRoles", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserRoles", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Roles", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleClaims", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleClaims", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "OnModelCreating", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.EntityFrameworkCore.ModelBuilder" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 4, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 5, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 6, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRoleClaim" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 7, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext, Microsoft.AspNetCore.Identity.IdentityUserLogin, Microsoft.AspNetCore.Identity.IdentityUserToken>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.EntityFrameworkCore.DbContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Users", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserClaims", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserClaims", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserLogins", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserLogins", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserTokens", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserTokens", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.EntityFrameworkCore.DbSet" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "OnModelCreating", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.EntityFrameworkCore.ModelBuilder" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.EntityFrameworkCore.DbContextOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 4, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.EntityFrameworkCore.DbContext" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore, Microsoft.AspNetCore.Identity.IdentityRoleClaim>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Microsoft.AspNetCore.Identity.IRoleClaimStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "id", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Context", + "Parameters": [], + "ReturnType": "T1", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ErrorDescriber", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AutoSaveChanges", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AutoSaveChanges", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdFromString", + "Parameters": [ + { + "Name": "id", + "Type": "System.String" + } + ], + "ReturnType": "T2", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdToString", + "Parameters": [ + { + "Name": "id", + "Type": "T2" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateRoleClaim", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "T4", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserRole" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRoleClaim" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.EntityFrameworkCore.DbContext" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore, Microsoft.AspNetCore.Identity.IdentityUserLogin, Microsoft.AspNetCore.Identity.IdentityUserToken>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserStoreBase", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IProtectedUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedUserName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Context", + "Parameters": [], + "ReturnType": "T1", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UsersSet", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserClaims", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserLogins", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserTokens", + "Parameters": [], + "ReturnType": "Microsoft.EntityFrameworkCore.DbSet", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AutoSaveChanges", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AutoSaveChanges", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SaveChanges", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T2" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T2" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T5" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T5" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T1" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 5, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.EntityFrameworkCore.DbContext" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.EntityFrameworkCore.DbContext" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T2" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore, Microsoft.AspNetCore.Identity.IdentityUserRole, Microsoft.AspNetCore.Identity.IdentityUserLogin, Microsoft.AspNetCore.Identity.IdentityUserToken, Microsoft.AspNetCore.Identity.IdentityRoleClaim>", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T2" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserStoreBase", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IProtectedUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedUserName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Context", + "Parameters": [], + "ReturnType": "T2", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AutoSaveChanges", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AutoSaveChanges", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SaveChanges", + "Parameters": [ + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindRoleAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserRoleAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T3" + }, + { + "Name": "roleId", + "Type": "T3" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T3" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T3" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T7" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T7" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "T2" + }, + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TContext", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "Microsoft.EntityFrameworkCore.DbContext" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 3, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 5, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 6, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 7, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 8, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRoleClaim" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/Identity/AspNetRoleManager.cs b/src/Identity/src/Identity/AspNetRoleManager.cs new file mode 100644 index 0000000000..374320bd28 --- /dev/null +++ b/src/Identity/src/Identity/AspNetRoleManager.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 System; +using System.Collections.Generic; +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the APIs for managing roles in a persistence store. + /// + /// The type encapsulating a role. + public class AspNetRoleManager : RoleManager, IDisposable where TRole : class + { + private readonly CancellationToken _cancel; + + /// + /// Constructs a new instance of . + /// + /// The persistence store the manager will operate over. + /// A collection of validators for roles. + /// The normalizer to use when normalizing role names to keys. + /// The used to provider error messages. + /// The logger used to log messages, warnings and errors. + /// The accessor used to access the . + public AspNetRoleManager(IRoleStore store, + IEnumerable> roleValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + ILogger> logger, + IHttpContextAccessor contextAccessor) + : base(store, roleValidators, keyNormalizer, errors, logger) + { + _cancel = contextAccessor?.HttpContext?.RequestAborted ?? CancellationToken.None; + } + + /// + /// The cancellation token associated with the current HttpContext.RequestAborted or CancellationToken.None if unavailable. + /// + protected override CancellationToken CancellationToken => _cancel; + } +} diff --git a/src/Identity/src/Identity/AspNetUserManager.cs b/src/Identity/src/Identity/AspNetUserManager.cs new file mode 100644 index 0000000000..f4f31e2856 --- /dev/null +++ b/src/Identity/src/Identity/AspNetUserManager.cs @@ -0,0 +1,53 @@ +// 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.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the APIs for managing user in a persistence store. + /// + /// The type encapsulating a user. + public class AspNetUserManager : UserManager, IDisposable where TUser : class + { + private readonly CancellationToken _cancel; + + /// + /// Constructs a new instance of . + /// + /// The persistence store the manager will operate over. + /// The accessor used to access the . + /// The password hashing implementation to use when saving passwords. + /// A collection of to validate users against. + /// A collection of to validate passwords against. + /// The to use when generating index keys for users. + /// The used to provider error messages. + /// The used to resolve services. + /// The logger used to log messages, warnings and errors. + public AspNetUserManager(IUserStore store, + IOptions optionsAccessor, + IPasswordHasher passwordHasher, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, + ILogger> logger) + : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) + { + _cancel = services?.GetService()?.HttpContext?.RequestAborted ?? CancellationToken.None; + } + + /// + /// The cancellation token associated with the current HttpContext.RequestAborted or CancellationToken.None if unavailable. + /// + protected override CancellationToken CancellationToken => _cancel; + } +} diff --git a/src/Identity/src/Identity/BuilderExtensions.cs b/src/Identity/src/Identity/BuilderExtensions.cs new file mode 100644 index 0000000000..e1f9defb43 --- /dev/null +++ b/src/Identity/src/Identity/BuilderExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Identity extensions for . + /// + public static class BuilderExtensions + { + /// + /// + /// This method is obsolete and will be removed in a future version. + /// The recommended alternative is + /// + /// + /// Enables ASP.NET identity for the current application. + /// + /// + /// The instance this method extends. + /// The instance this method extends. + [Obsolete( + "This method is obsolete and will be removed in a future version. " + + "The recommended alternative is UseAuthentication(). " + + "See https://go.microsoft.com/fwlink/?linkid=845470")] + public static IApplicationBuilder UseIdentity(this IApplicationBuilder app) + => app.UseAuthentication(); + } +} diff --git a/src/Identity/src/Identity/DataProtectionTokenProvider.cs b/src/Identity/src/Identity/DataProtectionTokenProvider.cs new file mode 100644 index 0000000000..b268cc142d --- /dev/null +++ b/src/Identity/src/Identity/DataProtectionTokenProvider.cs @@ -0,0 +1,192 @@ +// 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.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides protection and validation of identity tokens. + /// + /// The type used to represent a user. + public class DataProtectorTokenProvider : IUserTwoFactorTokenProvider where TUser : class + { + /// + /// Initializes a new instance of the class. + /// + /// The system data protection provider. + /// The configured . + public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions options) + { + if (dataProtectionProvider == null) + { + throw new ArgumentNullException(nameof(dataProtectionProvider)); + } + Options = options?.Value ?? new DataProtectionTokenProviderOptions(); + // Use the Name as the purpose which should usually be distinct from others + Protector = dataProtectionProvider.CreateProtector(Name ?? "DataProtectorTokenProvider"); + } + + /// + /// Gets the for this instance. + /// + /// + /// The for this instance. + /// + protected DataProtectionTokenProviderOptions Options { get; private set; } + + /// + /// Gets the for this instance. + /// + /// + /// The for this instance. + /// + protected IDataProtector Protector { get; private set; } + + /// + /// Gets the name of this instance. + /// + /// + /// The name of this instance. + /// + public string Name { get { return Options.Name; } } + + /// + /// Generates a protected token for the specified as an asynchronous operation. + /// + /// The purpose the token will be used for. + /// The to retrieve user properties from. + /// The the token will be generated from. + /// A representing the generated token. + public virtual async Task GenerateAsync(string purpose, UserManager manager, TUser user) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var ms = new MemoryStream(); + var userId = await manager.GetUserIdAsync(user); + using (var writer = ms.CreateWriter()) + { + writer.Write(DateTimeOffset.UtcNow); + writer.Write(userId); + writer.Write(purpose ?? ""); + string stamp = null; + if (manager.SupportsUserSecurityStamp) + { + stamp = await manager.GetSecurityStampAsync(user); + } + writer.Write(stamp ?? ""); + } + var protectedBytes = Protector.Protect(ms.ToArray()); + return Convert.ToBase64String(protectedBytes); + } + + /// + /// Validates the protected for the specified and as an asynchronous operation. + /// + /// The purpose the token was be used for. + /// The token to validate. + /// The to retrieve user properties from. + /// The the token was generated for. + /// + /// A that represents the result of the asynchronous validation, + /// containing true if the token is valid, otherwise false. + /// + public virtual async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + { + try + { + var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token)); + var ms = new MemoryStream(unprotectedData); + using (var reader = ms.CreateReader()) + { + var creationTime = reader.ReadDateTimeOffset(); + var expirationTime = creationTime + Options.TokenLifespan; + if (expirationTime < DateTimeOffset.UtcNow) + { + return false; + } + + var userId = reader.ReadString(); + var actualUserId = await manager.GetUserIdAsync(user); + if (userId != actualUserId) + { + return false; + } + var purp = reader.ReadString(); + if (!string.Equals(purp, purpose)) + { + return false; + } + var stamp = reader.ReadString(); + if (reader.PeekChar() != -1) + { + return false; + } + + if (manager.SupportsUserSecurityStamp) + { + return stamp == await manager.GetSecurityStampAsync(user); + } + return stamp == ""; + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch + { + // Do not leak exception + } + return false; + } + + /// + /// Returns a indicating whether a token generated by this instance + /// can be used as a Two Factor Authentication token as an asynchronous operation. + /// + /// The to retrieve user properties from. + /// The the token was generated for. + /// + /// A that represents the result of the asynchronous query, + /// containing true if a token generated by this instance can be used as a Two Factor Authentication token, otherwise false. + /// + /// This method will always return false for instances of . + public virtual Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + { + return Task.FromResult(false); + } + } + + /// + /// Utility extensions to streams + /// + internal static class StreamExtensions + { + internal static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true); + + public static BinaryReader CreateReader(this Stream stream) + { + return new BinaryReader(stream, DefaultEncoding, true); + } + + public static BinaryWriter CreateWriter(this Stream stream) + { + return new BinaryWriter(stream, DefaultEncoding, true); + } + + public static DateTimeOffset ReadDateTimeOffset(this BinaryReader reader) + { + return new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero); + } + + public static void Write(this BinaryWriter writer, DateTimeOffset value) + { + writer.Write(value.UtcTicks); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/DataProtectionTokenProviderOptions.cs b/src/Identity/src/Identity/DataProtectionTokenProviderOptions.cs new file mode 100644 index 0000000000..73eee0c115 --- /dev/null +++ b/src/Identity/src/Identity/DataProtectionTokenProviderOptions.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Contains options for the . + /// + public class DataProtectionTokenProviderOptions + { + /// + /// Gets or sets the name of the . Defaults to DataProtectorTokenProvider. + /// + /// + /// The name of the . + /// + public string Name { get; set; } = "DataProtectorTokenProvider"; + + /// + /// Gets or sets the amount of time a generated token remains valid. Defaults to 1 day. + /// + /// + /// The amount of time a generated token remains valid. + /// + public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays(1); + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/ExternalLoginInfo.cs b/src/Identity/src/Identity/ExternalLoginInfo.cs new file mode 100644 index 0000000000..e7305324f9 --- /dev/null +++ b/src/Identity/src/Identity/ExternalLoginInfo.cs @@ -0,0 +1,39 @@ +// 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 System.Security.Claims; +using Microsoft.AspNetCore.Authentication; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents login information, source and externally source principal for a user record + /// + public class ExternalLoginInfo : UserLoginInfo + { + /// + /// Creates a new instance of + /// + /// The to associate with this login. + /// The provider associated with this login information. + /// The unique identifier for this user provided by the login provider. + /// The display name for this user provided by the login provider. + public ExternalLoginInfo(ClaimsPrincipal principal, string loginProvider, string providerKey, + string displayName) : base(loginProvider, providerKey, displayName) + { + Principal = principal; + } + + /// + /// Gets or sets the associated with this login. + /// + /// The associated with this login. + public ClaimsPrincipal Principal { get; set; } + + /// + /// The s associated with this login. + /// + public IEnumerable AuthenticationTokens { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/ISecurityStampValidator.cs b/src/Identity/src/Identity/ISecurityStampValidator.cs new file mode 100644 index 0000000000..f24b76a6ee --- /dev/null +++ b/src/Identity/src/Identity/ISecurityStampValidator.cs @@ -0,0 +1,25 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.Cookies; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides an abstraction for a validating a security stamp of an incoming identity, and regenerating or rejecting the + /// identity based on the validation result. + /// + public interface ISecurityStampValidator + { + /// + /// Validates a security stamp of an identity as an asynchronous operation, and rebuilds the identity if the validation succeeds, otherwise rejects + /// the identity. + /// + /// The context containing the + /// and to validate. + /// The that represents the asynchronous validation operation. + Task ValidateAsync(CookieValidatePrincipalContext context); + } +} + diff --git a/src/Identity/src/Identity/ITwoFactorSecurityStampValidator.cs b/src/Identity/src/Identity/ITwoFactorSecurityStampValidator.cs new file mode 100644 index 0000000000..3d624b190d --- /dev/null +++ b/src/Identity/src/Identity/ITwoFactorSecurityStampValidator.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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to validate the two factor remember client cookie security stamp. + /// + public interface ITwoFactorSecurityStampValidator : ISecurityStampValidator + { } +} + diff --git a/src/Identity/src/Identity/IdentityBuilderExtensions.cs b/src/Identity/src/Identity/IdentityBuilderExtensions.cs new file mode 100644 index 0000000000..df0a1c59d0 --- /dev/null +++ b/src/Identity/src/Identity/IdentityBuilderExtensions.cs @@ -0,0 +1,77 @@ +// 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.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Helper functions for configuring identity services. + /// + public static class IdentityBuilderExtensions + { + /// + /// Adds the default token providers used to generate tokens for reset passwords, change email + /// and change telephone number operations, and for two factor authentication token generation. + /// + /// The current instance. + /// The current instance. + public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder) + { + var userType = builder.UserType; + var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType); + var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType); + var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType); + var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType); + return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType) + .AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType) + .AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType) + .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType); + } + + private static void AddSignInManagerDeps(this IdentityBuilder builder) + { + builder.Services.AddHttpContextAccessor(); + builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(builder.UserType)); + builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(builder.UserType)); + } + + /// + /// Adds a for the . + /// + /// The current instance. + /// The current instance. + public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) + { + builder.AddSignInManagerDeps(); + var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); + builder.Services.AddScoped(managerType); + return builder; + } + + /// + /// Adds a for the . + /// + /// The type of the sign in manager to add. + /// The current instance. + /// The current instance. + public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) where TSignInManager : class + { + builder.AddSignInManagerDeps(); + var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); + var customType = typeof(TSignInManager); + if (!managerType.GetTypeInfo().IsAssignableFrom(customType.GetTypeInfo())) + { + throw new InvalidOperationException(Resources.FormatInvalidManagerType(customType.Name, "SignInManager", builder.UserType.Name)); + } + if (managerType != customType) + { + builder.Services.AddScoped(typeof(TSignInManager), services => services.GetRequiredService(managerType)); + } + builder.Services.AddScoped(managerType, typeof(TSignInManager)); + return builder; + } + } +} diff --git a/src/Identity/src/Identity/IdentityConstants.cs b/src/Identity/src/Identity/IdentityConstants.cs new file mode 100644 index 0000000000..e01715dfbc --- /dev/null +++ b/src/Identity/src/Identity/IdentityConstants.cs @@ -0,0 +1,32 @@ +// 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. + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents all the options you can use to configure the cookies middleware used by the identity system. + /// + public class IdentityConstants + { + private static readonly string CookiePrefix = "Identity"; + /// + /// The scheme used to identify application authentication cookies. + /// + public static readonly string ApplicationScheme = CookiePrefix + ".Application"; + + /// + /// The scheme used to identify external authentication cookies. + /// + public static readonly string ExternalScheme = CookiePrefix + ".External"; + + /// + /// The scheme used to identify Two Factor authentication cookies for saving the Remember Me state. + /// + public static readonly string TwoFactorRememberMeScheme = CookiePrefix + ".TwoFactorRememberMe"; + + /// + /// The scheme used to identify Two Factor authentication cookies for round tripping user identities. + /// + public static readonly string TwoFactorUserIdScheme = CookiePrefix + ".TwoFactorUserId"; + } +} diff --git a/src/Identity/src/Identity/IdentityCookiesBuilder.cs b/src/Identity/src/Identity/IdentityCookiesBuilder.cs new file mode 100644 index 0000000000..9ceebf823e --- /dev/null +++ b/src/Identity/src/Identity/IdentityCookiesBuilder.cs @@ -0,0 +1,34 @@ +// 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.Authentication.Cookies; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to configure identity cookie options. + /// + public class IdentityCookiesBuilder + { + /// + /// Used to configure the application cookie. + /// + public OptionsBuilder ApplicationCookie { get; set; } + + /// + /// Used to configure the external cookie. + /// + public OptionsBuilder ExternalCookie { get; set; } + + /// + /// Used to configure the two factor remember me cookie. + /// + public OptionsBuilder TwoFactorRememberMeCookie { get; set; } + + /// + /// Used to configure the two factor user id cookie. + /// + public OptionsBuilder TwoFactorUserIdCookie { get; set; } + } +} diff --git a/src/Identity/src/Identity/IdentityCookiesBuilderExtensions.cs b/src/Identity/src/Identity/IdentityCookiesBuilderExtensions.cs new file mode 100644 index 0000000000..65d32d337b --- /dev/null +++ b/src/Identity/src/Identity/IdentityCookiesBuilderExtensions.cs @@ -0,0 +1,103 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Helper functions for configuring identity services. + /// + public static class IdentityCookieAuthenticationBuilderExtensions + { + /// + /// Adds cookie authentication. + /// + /// The current instance. + /// The which can be used to configure the identity cookies. + public static IdentityCookiesBuilder AddIdentityCookies(this AuthenticationBuilder builder) + => builder.AddIdentityCookies(o => { }); + + /// + /// Adds the cookie authentication needed for sign in manager. + /// + /// The current instance. + /// Action used to configure the cookies. + /// The which can be used to configure the identity cookies. + public static IdentityCookiesBuilder AddIdentityCookies(this AuthenticationBuilder builder, Action configureCookies) + { + var cookieBuilder = new IdentityCookiesBuilder(); + cookieBuilder.ApplicationCookie = builder.AddApplicationCookie(); + cookieBuilder.ExternalCookie = builder.AddExternalCookie(); + cookieBuilder.TwoFactorRememberMeCookie = builder.AddTwoFactorRememberMeCookie(); + cookieBuilder.TwoFactorUserIdCookie = builder.AddTwoFactorUserIdCookie(); + configureCookies?.Invoke(cookieBuilder); + return cookieBuilder; + } + + /// + /// Adds the identity application cookie. + /// + /// The current instance. + /// The which can be used to configure the cookie authentication. + public static OptionsBuilder AddApplicationCookie(this AuthenticationBuilder builder) + { + builder.AddCookie(IdentityConstants.ApplicationScheme, o => + { + o.LoginPath = new PathString("/Account/Login"); + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync + }; + }); + return new OptionsBuilder(builder.Services, IdentityConstants.ApplicationScheme); + } + + /// + /// Adds the identity cookie used for external logins. + /// + /// The current instance. + /// The which can be used to configure the cookie authentication. + public static OptionsBuilder AddExternalCookie(this AuthenticationBuilder builder) + { + builder.AddCookie(IdentityConstants.ExternalScheme, o => + { + o.Cookie.Name = IdentityConstants.ExternalScheme; + o.ExpireTimeSpan = TimeSpan.FromMinutes(5); + }); + return new OptionsBuilder(builder.Services, IdentityConstants.ExternalScheme); + } + + /// + /// Adds the identity cookie used for two factor remember me. + /// + /// The current instance. + /// The which can be used to configure the cookie authentication. + public static OptionsBuilder AddTwoFactorRememberMeCookie(this AuthenticationBuilder builder) + { + builder.AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o => o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme); + return new OptionsBuilder(builder.Services, IdentityConstants.TwoFactorRememberMeScheme); + } + + /// + /// Adds the identity cookie used for two factor logins. + /// + /// The current instance. + /// The which can be used to configure the cookie authentication. + public static OptionsBuilder AddTwoFactorUserIdCookie(this AuthenticationBuilder builder) + { + builder.AddCookie(IdentityConstants.TwoFactorUserIdScheme, o => + { + o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme; + o.ExpireTimeSpan = TimeSpan.FromMinutes(5); + }); + return new OptionsBuilder(builder.Services, IdentityConstants.TwoFactorUserIdScheme); + } + } +} diff --git a/src/Identity/src/Identity/IdentityServiceCollectionExtensions.cs b/src/Identity/src/Identity/IdentityServiceCollectionExtensions.cs new file mode 100644 index 0000000000..c941fdd201 --- /dev/null +++ b/src/Identity/src/Identity/IdentityServiceCollectionExtensions.cs @@ -0,0 +1,122 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Contains extension methods to for configuring identity services. + /// + public static class IdentityServiceCollectionExtensions + { + /// + /// Adds the default identity system configuration for the specified User and Role types. + /// + /// The type representing a User in the system. + /// The type representing a Role in the system. + /// The services available in the application. + /// An for creating and configuring the identity system. + public static IdentityBuilder AddIdentity( + this IServiceCollection services) + where TUser : class + where TRole : class + => services.AddIdentity(setupAction: null); + + /// + /// Adds and configures the identity system for the specified User and Role types. + /// + /// The type representing a User in the system. + /// The type representing a Role in the system. + /// The services available in the application. + /// An action to configure the . + /// An for creating and configuring the identity system. + public static IdentityBuilder AddIdentity( + this IServiceCollection services, + Action setupAction) + where TUser : class + where TRole : class + { + // Services used by identity + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; + options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddCookie(IdentityConstants.ApplicationScheme, o => + { + o.LoginPath = new PathString("/Account/Login"); + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync + }; + }) + .AddCookie(IdentityConstants.ExternalScheme, o => + { + o.Cookie.Name = IdentityConstants.ExternalScheme; + o.ExpireTimeSpan = TimeSpan.FromMinutes(5); + }) + .AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o => + { + o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme; + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = SecurityStampValidator.ValidateAsync + }; + }) + .AddCookie(IdentityConstants.TwoFactorUserIdScheme, o => + { + o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme; + o.ExpireTimeSpan = TimeSpan.FromMinutes(5); + }); + + // Hosting doesn't add IHttpContextAccessor by default + services.AddHttpContextAccessor(); + // Identity services + services.TryAddScoped, UserValidator>(); + services.TryAddScoped, PasswordValidator>(); + services.TryAddScoped, PasswordHasher>(); + services.TryAddScoped(); + services.TryAddScoped, RoleValidator>(); + // No interface for the error describer so we can add errors without rev'ing the interface + services.TryAddScoped(); + services.TryAddScoped>(); + services.TryAddScoped>(); + services.TryAddScoped, UserClaimsPrincipalFactory>(); + services.TryAddScoped, AspNetUserManager>(); + services.TryAddScoped, SignInManager>(); + services.TryAddScoped, AspNetRoleManager>(); + + if (setupAction != null) + { + services.Configure(setupAction); + } + + return new IdentityBuilder(typeof(TUser), typeof(TRole), services); + } + + /// + /// Configures the application cookie. + /// + /// The services available in the application. + /// An action to configure the . + /// The services. + public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action configure) + => services.Configure(IdentityConstants.ApplicationScheme, configure); + + /// + /// Configure the external cookie. + /// + /// The services available in the application. + /// An action to configure the . + /// The services. + public static IServiceCollection ConfigureExternalCookie(this IServiceCollection services, Action configure) + => services.Configure(IdentityConstants.ExternalScheme, configure); + + } +} diff --git a/src/Identity/src/Identity/Microsoft.AspNetCore.Identity.csproj b/src/Identity/src/Identity/Microsoft.AspNetCore.Identity.csproj new file mode 100644 index 0000000000..fc0fae70c8 --- /dev/null +++ b/src/Identity/src/Identity/Microsoft.AspNetCore.Identity.csproj @@ -0,0 +1,20 @@ + + + + ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. ASP.NET Core Identity allows you to add login features to your application and makes it easy to customize data about the logged in user. + netstandard2.0 + true + aspnetcore;identity;membership + + + + + + + + + + + + + diff --git a/src/Identity/src/Identity/Properties/AssemblyInfo.cs b/src/Identity/src/Identity/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c81ac16167 --- /dev/null +++ b/src/Identity/src/Identity/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +// 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.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Specification.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Identity.InMemory.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Identity/src/Identity/Properties/Resources.Designer.cs b/src/Identity/src/Identity/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..afd02fc138 --- /dev/null +++ b/src/Identity/src/Identity/Properties/Resources.Designer.cs @@ -0,0 +1,72 @@ +// +namespace Microsoft.AspNetCore.Identity +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNetCore.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// Type {0} must derive from {1}<{2}>. + /// + internal static string InvalidManagerType + { + get => GetString("InvalidManagerType"); + } + + /// + /// Type {0} must derive from {1}<{2}>. + /// + internal static string FormatInvalidManagerType(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidManagerType"), p0, p1, p2); + + /// + /// The provided PasswordHasherCompatibilityMode is invalid. + /// + internal static string InvalidPasswordHasherCompatibilityMode + { + get => GetString("InvalidPasswordHasherCompatibilityMode"); + } + + /// + /// The provided PasswordHasherCompatibilityMode is invalid. + /// + internal static string FormatInvalidPasswordHasherCompatibilityMode() + => GetString("InvalidPasswordHasherCompatibilityMode"); + + /// + /// The iteration count must be a positive integer. + /// + internal static string InvalidPasswordHasherIterationCount + { + get => GetString("InvalidPasswordHasherIterationCount"); + } + + /// + /// The iteration count must be a positive integer. + /// + internal static string FormatInvalidPasswordHasherIterationCount() + => GetString("InvalidPasswordHasherIterationCount"); + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Identity/src/Identity/Properties/debugSettings.json b/src/Identity/src/Identity/Properties/debugSettings.json new file mode 100644 index 0000000000..a44fad34a3 --- /dev/null +++ b/src/Identity/src/Identity/Properties/debugSettings.json @@ -0,0 +1,3 @@ +{ + "Profiles": [] +} \ No newline at end of file diff --git a/src/Identity/src/Identity/Resources.resx b/src/Identity/src/Identity/Resources.resx new file mode 100644 index 0000000000..0c4e8fddfe --- /dev/null +++ b/src/Identity/src/Identity/Resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Type {0} must derive from {1}<{2}>. + Error when the manager type is not derived correctly + + + The provided PasswordHasherCompatibilityMode is invalid. + Error when the password hasher doesn't understand the format it's being asked to produce. + + + The iteration count must be a positive integer. + Error when the iteration count is < 1. + + \ No newline at end of file diff --git a/src/Identity/src/Identity/SecurityStampRefreshingPrincipalContext.cs b/src/Identity/src/Identity/SecurityStampRefreshingPrincipalContext.cs new file mode 100644 index 0000000000..78b67463e3 --- /dev/null +++ b/src/Identity/src/Identity/SecurityStampRefreshingPrincipalContext.cs @@ -0,0 +1,23 @@ +// 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.Security.Claims; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Used to pass information during the SecurityStamp validation event. + /// + public class SecurityStampRefreshingPrincipalContext + { + /// + /// The principal contained in the current cookie. + /// + public ClaimsPrincipal CurrentPrincipal { get; set; } + + /// + /// The new principal which should replace the current. + /// + public ClaimsPrincipal NewPrincipal { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/SecurityStampValidator.cs b/src/Identity/src/Identity/SecurityStampValidator.cs new file mode 100644 index 0000000000..9a9ecbe139 --- /dev/null +++ b/src/Identity/src/Identity/SecurityStampValidator.cs @@ -0,0 +1,166 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides default implementation of validation functions for security stamps. + /// + /// The type encapsulating a user. + public class SecurityStampValidator : ISecurityStampValidator where TUser : class + { + /// + /// Creates a new instance of . + /// + /// Used to access the . + /// The . + /// The system clock. + public SecurityStampValidator(IOptions options, SignInManager signInManager, ISystemClock clock) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + if (signInManager == null) + { + throw new ArgumentNullException(nameof(signInManager)); + } + SignInManager = signInManager; + Options = options.Value; + Clock = clock; + } + + /// + /// The SignInManager. + /// + public SignInManager SignInManager { get; } + + /// + /// The . + /// + public SecurityStampValidatorOptions Options { get; } + + /// + /// The . + /// + public ISystemClock Clock { get; } + + /// + /// Called when the security stamp has been verified. + /// + /// The user who has been verified. + /// The . + /// A task. + protected virtual async Task SecurityStampVerified(TUser user, CookieValidatePrincipalContext context) + { + var newPrincipal = await SignInManager.CreateUserPrincipalAsync(user); + + if (Options.OnRefreshingPrincipal != null) + { + var replaceContext = new SecurityStampRefreshingPrincipalContext + { + CurrentPrincipal = context.Principal, + NewPrincipal = newPrincipal + }; + + // Note: a null principal is allowed and results in a failed authentication. + await Options.OnRefreshingPrincipal(replaceContext); + newPrincipal = replaceContext.NewPrincipal; + } + + // REVIEW: note we lost login authentication method + context.ReplacePrincipal(newPrincipal); + context.ShouldRenew = true; + } + + /// + /// Verifies the principal's security stamp, returns the matching user if successful + /// + /// The principal to verify. + /// The verified user or null if verification fails. + protected virtual Task VerifySecurityStamp(ClaimsPrincipal principal) + => SignInManager.ValidateSecurityStampAsync(principal); + + /// + /// Validates a security stamp of an identity as an asynchronous operation, and rebuilds the identity if the validation succeeds, otherwise rejects + /// the identity. + /// + /// The context containing the + /// and to validate. + /// The that represents the asynchronous validation operation. + public virtual async Task ValidateAsync(CookieValidatePrincipalContext context) + { + var currentUtc = DateTimeOffset.UtcNow; + if (context.Options != null && Clock != null) + { + currentUtc = Clock.UtcNow; + } + var issuedUtc = context.Properties.IssuedUtc; + + // Only validate if enough time has elapsed + var validate = (issuedUtc == null); + if (issuedUtc != null) + { + var timeElapsed = currentUtc.Subtract(issuedUtc.Value); + validate = timeElapsed > Options.ValidationInterval; + } + if (validate) + { + var user = await VerifySecurityStamp(context.Principal); + if (user != null) + { + await SecurityStampVerified(user, context); + } + else + { + context.RejectPrincipal(); + await SignInManager.SignOutAsync(); + } + } + } + } + + /// + /// Static helper class used to configure a CookieAuthenticationNotifications to validate a cookie against a user's security + /// stamp. + /// + public static class SecurityStampValidator + { + /// + /// Validates a principal against a user's stored security stamp. + /// + /// The context containing the + /// and to validate. + /// The that represents the asynchronous validation operation. + public static Task ValidatePrincipalAsync(CookieValidatePrincipalContext context) + => ValidateAsync(context); + + /// + /// Used to validate the and + /// cookies against the user's + /// stored security stamp. + /// + /// The context containing the + /// and to validate. + /// + + public static Task ValidateAsync(CookieValidatePrincipalContext context) where TValidator : ISecurityStampValidator + { + if (context.HttpContext.RequestServices == null) + { + throw new InvalidOperationException("RequestServices is null."); + } + + var validator = context.HttpContext.RequestServices.GetRequiredService(); + return validator.ValidateAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/SecurityStampValidatorOptions.cs b/src/Identity/src/Identity/SecurityStampValidatorOptions.cs new file mode 100644 index 0000000000..c419819bb8 --- /dev/null +++ b/src/Identity/src/Identity/SecurityStampValidatorOptions.cs @@ -0,0 +1,27 @@ +// 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.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Options for . + /// + public class SecurityStampValidatorOptions + { + /// + /// Gets or sets the after which security stamps are re-validated. Defaults to 30 minutes. + /// + /// + /// The after which security stamps are re-validated. + /// + public TimeSpan ValidationInterval { get; set; } = TimeSpan.FromMinutes(30); + + /// + /// Invoked when the default security stamp validator replaces the user's ClaimsPrincipal in the cookie. + /// + public Func OnRefreshingPrincipal { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/SignInManager.cs b/src/Identity/src/Identity/SignInManager.cs new file mode 100644 index 0000000000..d553cd71c9 --- /dev/null +++ b/src/Identity/src/Identity/SignInManager.cs @@ -0,0 +1,827 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Provides the APIs for user sign in. + /// + /// The type encapsulating a user. + public class SignInManager where TUser : class + { + private const string LoginProviderKey = "LoginProvider"; + private const string XsrfKey = "XsrfId"; + + /// + /// Creates a new instance of . + /// + /// An instance of used to retrieve users from and persist users. + /// The accessor used to access the . + /// The factory to use to create claims principals for a user. + /// The accessor used to access the . + /// The logger used to log messages, warnings and errors. + /// The scheme provider that is used enumerate the authentication schemes. + public SignInManager(UserManager userManager, + IHttpContextAccessor contextAccessor, + IUserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + ILogger> logger, + IAuthenticationSchemeProvider schemes) + { + if (userManager == null) + { + throw new ArgumentNullException(nameof(userManager)); + } + if (contextAccessor == null) + { + throw new ArgumentNullException(nameof(contextAccessor)); + } + if (claimsFactory == null) + { + throw new ArgumentNullException(nameof(claimsFactory)); + } + + UserManager = userManager; + _contextAccessor = contextAccessor; + ClaimsFactory = claimsFactory; + Options = optionsAccessor?.Value ?? new IdentityOptions(); + Logger = logger; + _schemes = schemes; + } + + private readonly IHttpContextAccessor _contextAccessor; + private HttpContext _context; + private IAuthenticationSchemeProvider _schemes; + + /// + /// Gets the used to log messages from the manager. + /// + /// + /// The used to log messages from the manager. + /// + public virtual ILogger Logger { get; set; } + + /// + /// The used. + /// + public UserManager UserManager { get; set; } + + /// + /// The used. + /// + public IUserClaimsPrincipalFactory ClaimsFactory { get; set; } + + /// + /// The used. + /// + public IdentityOptions Options { get; set; } + + /// + /// The used. + /// + public HttpContext Context + { + get + { + var context = _context ?? _contextAccessor?.HttpContext; + if (context == null) + { + throw new InvalidOperationException("HttpContext must not be null."); + } + return context; + } + set + { + _context = value; + } + } + + /// + /// Creates a for the specified , as an asynchronous operation. + /// + /// The user to create a for. + /// The task object representing the asynchronous operation, containing the ClaimsPrincipal for the specified user. + public virtual async Task CreateUserPrincipalAsync(TUser user) => await ClaimsFactory.CreateAsync(user); + + /// + /// Returns true if the principal has an identity with the application cookie identity + /// + /// The instance. + /// True if the user is logged in with identity. + public virtual bool IsSignedIn(ClaimsPrincipal principal) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + return principal?.Identities != null && + principal.Identities.Any(i => i.AuthenticationType == IdentityConstants.ApplicationScheme); + } + + /// + /// Returns a flag indicating whether the specified user can sign in. + /// + /// The user whose sign-in status should be returned. + /// + /// The task object representing the asynchronous operation, containing a flag that is true + /// if the specified user can sign-in, otherwise false. + /// + public virtual async Task CanSignInAsync(TUser user) + { + if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user))) + { + Logger.LogWarning(0, "User {userId} cannot sign in without a confirmed email.", await UserManager.GetUserIdAsync(user)); + return false; + } + if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user))) + { + Logger.LogWarning(1, "User {userId} cannot sign in without a confirmed phone number.", await UserManager.GetUserIdAsync(user)); + return false; + } + + return true; + } + + /// + /// Regenerates the user's application cookie, whilst preserving the existing + /// AuthenticationProperties like rememberMe, as an asynchronous operation. + /// + /// The user whose sign-in cookie should be refreshed. + /// The task object representing the asynchronous operation. + public virtual async Task RefreshSignInAsync(TUser user) + { + var auth = await Context.AuthenticateAsync(IdentityConstants.ApplicationScheme); + var authenticationMethod = auth?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod); + await SignInAsync(user, auth?.Properties, authenticationMethod); + } + + /// + /// Signs in the specified . + /// + /// The user to sign-in. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Name of the method used to authenticate the user. + /// The task object representing the asynchronous operation. + public virtual Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null) + { + return SignInAsync(user, new AuthenticationProperties { IsPersistent = isPersistent }, authenticationMethod); + } + + /// + /// Signs in the specified . + /// + /// The user to sign-in. + /// Properties applied to the login and authentication cookie. + /// Name of the method used to authenticate the user. + /// The task object representing the asynchronous operation. + public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null) + { + var userPrincipal = await CreateUserPrincipalAsync(user); + // Review: should we guard against CreateUserPrincipal returning null? + if (authenticationMethod != null) + { + userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod)); + } + await Context.SignInAsync(IdentityConstants.ApplicationScheme, + userPrincipal, + authenticationProperties ?? new AuthenticationProperties()); + } + + /// + /// Signs the current user out of the application. + /// + public virtual async Task SignOutAsync() + { + await Context.SignOutAsync(IdentityConstants.ApplicationScheme); + await Context.SignOutAsync(IdentityConstants.ExternalScheme); + await Context.SignOutAsync(IdentityConstants.TwoFactorUserIdScheme); + } + + /// + /// Validates the security stamp for the specified against + /// the persisted stamp for the current user, as an asynchronous operation. + /// + /// The principal whose stamp should be validated. + /// The task object representing the asynchronous operation. The task will contain the + /// if the stamp matches the persisted value, otherwise it will return false. + public virtual async Task ValidateSecurityStampAsync(ClaimsPrincipal principal) + { + if (principal == null) + { + return null; + } + var user = await UserManager.GetUserAsync(principal); + if (await ValidateSecurityStampAsync(user, principal.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType))) + { + return user; + } + return null; + } + + /// + /// Validates the security stamp for the specified from one of + /// the two factor principals (remember client or user id) against + /// the persisted stamp for the current user, as an asynchronous operation. + /// + /// The principal whose stamp should be validated. + /// The task object representing the asynchronous operation. The task will contain the + /// if the stamp matches the persisted value, otherwise it will return false. + public virtual async Task ValidateTwoFactorSecurityStampAsync(ClaimsPrincipal principal) + { + if (principal == null || principal.Identity?.Name == null) + { + return null; + } + var user = await UserManager.FindByIdAsync(principal.Identity.Name); + if (await ValidateSecurityStampAsync(user, principal.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType))) + { + return user; + } + return null; + } + + /// + /// Validates the security stamp for the specified . Will always return false + /// if the userManager does not support security stamps. + /// + /// The user whose stamp should be validated. + /// The expected security stamp value. + /// True if the stamp matches the persisted value, otherwise it will return false. + public virtual async Task ValidateSecurityStampAsync(TUser user, string securityStamp) + => user != null && UserManager.SupportsUserSecurityStamp + && securityStamp == await UserManager.GetSecurityStampAsync(user); + + /// + /// Attempts to sign in the specified and combination + /// as an asynchronous operation. + /// + /// The user to sign in. + /// The password to attempt to sign in with. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Flag indicating if the user account should be locked if the sign in fails. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task PasswordSignInAsync(TUser user, string password, + bool isPersistent, bool lockoutOnFailure) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var attempt = await CheckPasswordSignInAsync(user, password, lockoutOnFailure); + return attempt.Succeeded + ? await SignInOrTwoFactorAsync(user, isPersistent) + : attempt; + } + + /// + /// Attempts to sign in the specified and combination + /// as an asynchronous operation. + /// + /// The user name to sign in. + /// The password to attempt to sign in with. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Flag indicating if the user account should be locked if the sign in fails. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task PasswordSignInAsync(string userName, string password, + bool isPersistent, bool lockoutOnFailure) + { + var user = await UserManager.FindByNameAsync(userName); + if (user == null) + { + return SignInResult.Failed; + } + + return await PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure); + } + + /// + /// Attempts a password sign in for a user. + /// + /// The user to sign in. + /// The password to attempt to sign in with. + /// Flag indicating if the user account should be locked if the sign in fails. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + /// + public virtual async Task CheckPasswordSignInAsync(TUser user, string password, bool lockoutOnFailure) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var error = await PreSignInCheck(user); + if (error != null) + { + return error; + } + + if (await UserManager.CheckPasswordAsync(user, password)) + { + var alwaysLockout = AppContext.TryGetSwitch("Microsoft.AspNetCore.Identity.CheckPasswordSignInAlwaysResetLockoutOnSuccess", out var enabled) && enabled; + // Only reset the lockout when TFA is not enabled when not in quirks mode + if (alwaysLockout || !await IsTfaEnabled(user)) + { + await ResetLockout(user); + } + + return SignInResult.Success; + } + Logger.LogWarning(2, "User {userId} failed to provide the correct password.", await UserManager.GetUserIdAsync(user)); + + if (UserManager.SupportsUserLockout && lockoutOnFailure) + { + // If lockout is requested, increment access failed count which might lock out the user + await UserManager.AccessFailedAsync(user); + if (await UserManager.IsLockedOutAsync(user)) + { + return await LockedOut(user); + } + } + return SignInResult.Failed; + } + + /// + /// Returns a flag indicating if the current client browser has been remembered by two factor authentication + /// for the user attempting to login, as an asynchronous operation. + /// + /// The user attempting to login. + /// + /// The task object representing the asynchronous operation containing true if the browser has been remembered + /// for the current user. + /// + public virtual async Task IsTwoFactorClientRememberedAsync(TUser user) + { + var userId = await UserManager.GetUserIdAsync(user); + var result = await Context.AuthenticateAsync(IdentityConstants.TwoFactorRememberMeScheme); + return (result?.Principal != null && result.Principal.FindFirstValue(ClaimTypes.Name) == userId); + } + + /// + /// Sets a flag on the browser to indicate the user has selected "Remember this browser" for two factor authentication purposes, + /// as an asynchronous operation. + /// + /// The user who choose "remember this browser". + /// The task object representing the asynchronous operation. + public virtual async Task RememberTwoFactorClientAsync(TUser user) + { + var principal = await StoreRememberClient(user); + await Context.SignInAsync(IdentityConstants.TwoFactorRememberMeScheme, + principal, + new AuthenticationProperties { IsPersistent = true }); + } + + /// + /// Clears the "Remember this browser flag" from the current browser, as an asynchronous operation. + /// + /// The task object representing the asynchronous operation. + public virtual Task ForgetTwoFactorClientAsync() + { + return Context.SignOutAsync(IdentityConstants.TwoFactorRememberMeScheme); + } + + /// + /// Signs in the user without two factor authentication using a two factor recovery code. + /// + /// The two factor recovery code. + /// + public virtual async Task TwoFactorRecoveryCodeSignInAsync(string recoveryCode) + { + var twoFactorInfo = await RetrieveTwoFactorInfoAsync(); + if (twoFactorInfo == null || twoFactorInfo.UserId == null) + { + return SignInResult.Failed; + } + var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId); + if (user == null) + { + return SignInResult.Failed; + } + + var result = await UserManager.RedeemTwoFactorRecoveryCodeAsync(user, recoveryCode); + if (result.Succeeded) + { + await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent: false, rememberClient: false); + return SignInResult.Success; + } + + // We don't protect against brute force attacks since codes are expected to be random. + return SignInResult.Failed; + } + + private async Task DoTwoFactorSignInAsync(TUser user, TwoFactorAuthenticationInfo twoFactorInfo, bool isPersistent, bool rememberClient) + { + // When token is verified correctly, clear the access failed count used for lockout + await ResetLockout(user); + + // Cleanup external cookie + if (twoFactorInfo.LoginProvider != null) + { + await Context.SignOutAsync(IdentityConstants.ExternalScheme); + } + // Cleanup two factor user id cookie + await Context.SignOutAsync(IdentityConstants.TwoFactorUserIdScheme); + if (rememberClient) + { + await RememberTwoFactorClientAsync(user); + } + await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider); + } + + /// + /// Validates the sign in code from an authenticator app and creates and signs in the user, as an asynchronous operation. + /// + /// The two factor authentication code to validate. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Flag indicating whether the current browser should be remember, suppressing all further + /// two factor authentication prompts. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task TwoFactorAuthenticatorSignInAsync(string code, bool isPersistent, bool rememberClient) + { + var twoFactorInfo = await RetrieveTwoFactorInfoAsync(); + if (twoFactorInfo == null || twoFactorInfo.UserId == null) + { + return SignInResult.Failed; + } + var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId); + if (user == null) + { + return SignInResult.Failed; + } + + var error = await PreSignInCheck(user); + if (error != null) + { + return error; + } + + if (await UserManager.VerifyTwoFactorTokenAsync(user, Options.Tokens.AuthenticatorTokenProvider, code)) + { + await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient); + return SignInResult.Success; + } + // If the token is incorrect, record the failure which also may cause the user to be locked out + await UserManager.AccessFailedAsync(user); + return SignInResult.Failed; + } + + /// + /// Validates the two factor sign in code and creates and signs in the user, as an asynchronous operation. + /// + /// The two factor authentication provider to validate the code against. + /// The two factor authentication code to validate. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Flag indicating whether the current browser should be remember, suppressing all further + /// two factor authentication prompts. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient) + { + var twoFactorInfo = await RetrieveTwoFactorInfoAsync(); + if (twoFactorInfo == null || twoFactorInfo.UserId == null) + { + return SignInResult.Failed; + } + var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId); + if (user == null) + { + return SignInResult.Failed; + } + + var error = await PreSignInCheck(user); + if (error != null) + { + return error; + } + if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code)) + { + await DoTwoFactorSignInAsync(user, twoFactorInfo, isPersistent, rememberClient); + return SignInResult.Success; + } + // If the token is incorrect, record the failure which also may cause the user to be locked out + await UserManager.AccessFailedAsync(user); + return SignInResult.Failed; + } + + /// + /// Gets the for the current two factor authentication login, as an asynchronous operation. + /// + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task GetTwoFactorAuthenticationUserAsync() + { + var info = await RetrieveTwoFactorInfoAsync(); + if (info == null) + { + return null; + } + + return await UserManager.FindByIdAsync(info.UserId); + } + + /// + /// Signs in a user via a previously registered third party login, as an asynchronous operation. + /// + /// The login provider to use. + /// The unique provider identifier for the user. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual Task ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent) + => ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent, bypassTwoFactor: false); + + /// + /// Signs in a user via a previously registered third party login, as an asynchronous operation. + /// + /// The login provider to use. + /// The unique provider identifier for the user. + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// Flag indicating whether to bypass two factor authentication. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent, bool bypassTwoFactor) + { + var user = await UserManager.FindByLoginAsync(loginProvider, providerKey); + if (user == null) + { + return SignInResult.Failed; + } + + var error = await PreSignInCheck(user); + if (error != null) + { + return error; + } + return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor); + } + + /// + /// Gets a collection of s for the known external login providers. + /// + /// A collection of s for the known external login providers. + public virtual async Task> GetExternalAuthenticationSchemesAsync() + { + var schemes = await _schemes.GetAllSchemesAsync(); + return schemes.Where(s => !string.IsNullOrEmpty(s.DisplayName)); + } + + /// + /// Gets the external login information for the current login, as an asynchronous operation. + /// + /// Flag indication whether a Cross Site Request Forgery token was expected in the current request. + /// The task object representing the asynchronous operation containing the + /// for the sign-in attempt. + public virtual async Task GetExternalLoginInfoAsync(string expectedXsrf = null) + { + var auth = await Context.AuthenticateAsync(IdentityConstants.ExternalScheme); + var items = auth?.Properties?.Items; + if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey)) + { + return null; + } + + if (expectedXsrf != null) + { + if (!items.ContainsKey(XsrfKey)) + { + return null; + } + var userId = items[XsrfKey] as string; + if (userId != expectedXsrf) + { + return null; + } + } + + var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier); + var provider = items[LoginProviderKey] as string; + if (providerKey == null || provider == null) + { + return null; + } + // TODO: display name gone?. Add [] indexer for Authproperties + return new ExternalLoginInfo(auth.Principal, provider, providerKey, provider) + { + AuthenticationTokens = auth.Properties.GetTokens() + }; + } + + /// + /// Stores any authentication tokens found in the external authentication cookie into the associated user. + /// + /// The information from the external login provider. + /// The that represents the asynchronous operation, containing the of the operation. + public virtual async Task UpdateExternalAuthenticationTokensAsync(ExternalLoginInfo externalLogin) + { + if (externalLogin == null) + { + throw new ArgumentNullException(nameof(externalLogin)); + } + + if (externalLogin.AuthenticationTokens != null && externalLogin.AuthenticationTokens.Any()) + { + var user = await UserManager.FindByLoginAsync(externalLogin.LoginProvider, externalLogin.ProviderKey); + if (user == null) + { + return IdentityResult.Failed(); + } + + foreach (var token in externalLogin.AuthenticationTokens) + { + var result = await UserManager.SetAuthenticationTokenAsync(user, externalLogin.LoginProvider, token.Name, token.Value); + if (!result.Succeeded) + { + return result; + } + } + } + + return IdentityResult.Success; + } + + /// + /// Configures the redirect URL and user identifier for the specified external login . + /// + /// The provider to configure. + /// The external login URL users should be redirected to during the login flow. + /// The current user's identifier, which will be used to provide CSRF protection. + /// A configured . + public virtual AuthenticationProperties ConfigureExternalAuthenticationProperties(string provider, string redirectUrl, string userId = null) + { + var properties = new AuthenticationProperties { RedirectUri = redirectUrl }; + properties.Items[LoginProviderKey] = provider; + if (userId != null) + { + properties.Items[XsrfKey] = userId; + } + return properties; + } + + /// + /// Creates a claims principal for the specified 2fa information. + /// + /// The user whose is logging in via 2fa. + /// The 2fa provider. + /// A containing the user 2fa information. + internal ClaimsPrincipal StoreTwoFactorInfo(string userId, string loginProvider) + { + var identity = new ClaimsIdentity(IdentityConstants.TwoFactorUserIdScheme); + identity.AddClaim(new Claim(ClaimTypes.Name, userId)); + if (loginProvider != null) + { + identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, loginProvider)); + } + return new ClaimsPrincipal(identity); + } + + internal async Task StoreRememberClient(TUser user) + { + var userId = await UserManager.GetUserIdAsync(user); + var rememberBrowserIdentity = new ClaimsIdentity(IdentityConstants.TwoFactorRememberMeScheme); + rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, userId)); + if (UserManager.SupportsUserSecurityStamp) + { + var stamp = await UserManager.GetSecurityStampAsync(user); + rememberBrowserIdentity.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType, stamp)); + } + return new ClaimsPrincipal(rememberBrowserIdentity); + } + + private ClaimsIdentity CreateIdentity(TwoFactorAuthenticationInfo info) + { + if (info == null) + { + return null; + } + var identity = new ClaimsIdentity(IdentityConstants.TwoFactorUserIdScheme); + identity.AddClaim(new Claim(ClaimTypes.Name, info.UserId)); + if (info.LoginProvider != null) + { + identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, info.LoginProvider)); + } + return identity; + } + + private async Task IsTfaEnabled(TUser user) + => UserManager.SupportsUserTwoFactor && + await UserManager.GetTwoFactorEnabledAsync(user) && + (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0; + + /// + /// Signs in the specified if is set to false. + /// Otherwise stores the for use after a two factor check. + /// + /// + /// Flag indicating whether the sign-in cookie should persist after the browser is closed. + /// The login provider to use. Default is null + /// Flag indicating whether to bypass two factor authentication. Default is false + /// Returns a + protected virtual async Task SignInOrTwoFactorAsync(TUser user, bool isPersistent, string loginProvider = null, bool bypassTwoFactor = false) + { + if (!bypassTwoFactor && await IsTfaEnabled(user)) + { + if (!await IsTwoFactorClientRememberedAsync(user)) + { + // Store the userId for use after two factor check + var userId = await UserManager.GetUserIdAsync(user); + await Context.SignInAsync(IdentityConstants.TwoFactorUserIdScheme, StoreTwoFactorInfo(userId, loginProvider)); + return SignInResult.TwoFactorRequired; + } + } + // Cleanup external cookie + if (loginProvider != null) + { + await Context.SignOutAsync(IdentityConstants.ExternalScheme); + } + await SignInAsync(user, isPersistent, loginProvider); + return SignInResult.Success; + } + + private async Task RetrieveTwoFactorInfoAsync() + { + var result = await Context.AuthenticateAsync(IdentityConstants.TwoFactorUserIdScheme); + if (result?.Principal != null) + { + return new TwoFactorAuthenticationInfo + { + UserId = result.Principal.FindFirstValue(ClaimTypes.Name), + LoginProvider = result.Principal.FindFirstValue(ClaimTypes.AuthenticationMethod) + }; + } + return null; + } + + /// + /// Used to determine if a user is considered locked out. + /// + /// The user. + /// Whether a user is considered locked out. + protected virtual async Task IsLockedOut(TUser user) + { + return UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user); + } + + /// + /// Returns a locked out SignInResult. + /// + /// The user. + /// A locked out SignInResult + protected virtual async Task LockedOut(TUser user) + { + Logger.LogWarning(3, "User {userId} is currently locked out.", await UserManager.GetUserIdAsync(user)); + return SignInResult.LockedOut; + } + + /// + /// Used to ensure that a user is allowed to sign in. + /// + /// The user + /// Null if the user should be allowed to sign in, otherwise the SignInResult why they should be denied. + protected virtual async Task PreSignInCheck(TUser user) + { + if (!await CanSignInAsync(user)) + { + return SignInResult.NotAllowed; + } + if (await IsLockedOut(user)) + { + return await LockedOut(user); + } + return null; + } + + /// + /// Used to reset a user's lockout count. + /// + /// The user + /// The that represents the asynchronous operation, containing the of the operation. + protected virtual Task ResetLockout(TUser user) + { + if (UserManager.SupportsUserLockout) + { + return UserManager.ResetAccessFailedCountAsync(user); + } + return Task.CompletedTask; + } + + internal class TwoFactorAuthenticationInfo + { + public string UserId { get; set; } + public string LoginProvider { get; set; } + } + } +} diff --git a/src/Identity/src/Identity/TwoFactorSecurityStampValidator.cs b/src/Identity/src/Identity/TwoFactorSecurityStampValidator.cs new file mode 100644 index 0000000000..7d0feb7d39 --- /dev/null +++ b/src/Identity/src/Identity/TwoFactorSecurityStampValidator.cs @@ -0,0 +1,44 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Responsible for validation of two factor identity cookie security stamp. + /// + /// The type encapsulating a user. + public class TwoFactorSecurityStampValidator : SecurityStampValidator, ITwoFactorSecurityStampValidator where TUser : class + { + /// + /// Creates a new instance of . + /// + /// Used to access the . + /// The . + /// The system clock. + public TwoFactorSecurityStampValidator(IOptions options, SignInManager signInManager, ISystemClock clock) : base(options, signInManager, clock) + { } + + /// + /// Verifies the principal's security stamp, returns the matching user if successful + /// + /// The principal to verify. + /// The verified user or null if verification fails. + protected override Task VerifySecurityStamp(ClaimsPrincipal principal) + => SignInManager.ValidateTwoFactorSecurityStampAsync(principal); + + /// + /// Called when the security stamp has been verified. + /// + /// The user who has been verified. + /// The . + /// A task. + protected override Task SecurityStampVerified(TUser user, CookieValidatePrincipalContext context) + => Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Identity/src/Identity/baseline.netcore.json b/src/Identity/src/Identity/baseline.netcore.json new file mode 100644 index 0000000000..c6dc616ad3 --- /dev/null +++ b/src/Identity/src/Identity/baseline.netcore.json @@ -0,0 +1,1927 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Identity, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "ConfigureApplicationCookie", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "configure", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfigureExternalCookie", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "configure", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.BuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseIdentity", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.AspNetRoleManager", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.RoleManager", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CancellationToken", + "Parameters": [], + "ReturnType": "System.Threading.CancellationToken", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IRoleStore" + }, + { + "Name": "roleValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + }, + { + "Name": "contextAccessor", + "Type": "Microsoft.AspNetCore.Http.IHttpContextAccessor" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.AspNetUserManager", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserManager", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CancellationToken", + "Parameters": [], + "ReturnType": "System.Threading.CancellationToken", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IUserStore" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "passwordHasher", + "Type": "Microsoft.AspNetCore.Identity.IPasswordHasher" + }, + { + "Name": "userValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "passwordValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "services", + "Type": "System.IServiceProvider" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.DataProtectorTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Protector", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtectionProvider", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + }, + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Name", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TokenLifespan", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TokenLifespan", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ExternalLoginInfo", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserLoginInfo", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Principal", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Principal", + "Parameters": [ + { + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AuthenticationTokens", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AuthenticationTokens", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddDefaultTokenProviders", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Identity.IdentityBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddSignInManager", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Identity.IdentityBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "New": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddSignInManager", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Identity.IdentityBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "New": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TSignInManager", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityConstants", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ApplicationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ExternalScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "TwoFactorRememberMeScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "TwoFactorUserIdScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityCookiesBuilder", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ApplicationCookie", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ApplicationCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Options.OptionsBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExternalCookie", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExternalCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Options.OptionsBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorRememberMeCookie", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TwoFactorRememberMeCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Options.OptionsBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorUserIdCookie", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TwoFactorUserIdCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Options.OptionsBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddIdentityCookies", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityCookiesBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddIdentityCookies", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureCookies", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityCookiesBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddApplicationCookie", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddExternalCookie", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddTwoFactorRememberMeCookie", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddTwoFactorUserIdCookie", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.Extensions.Options.OptionsBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ISecurityStampValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ITwoFactorSecurityStampValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ISecurityStampValidator" + ], + "Members": [], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CurrentPrincipal", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CurrentPrincipal", + "Parameters": [ + { + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NewPrincipal", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NewPrincipal", + "Parameters": [ + { + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ISecurityStampValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_SignInManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Clock", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SecurityStampVerified", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifySecurityStamp", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.ISecurityStampValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "signInManager", + "Type": "Microsoft.AspNetCore.Identity.SignInManager" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampValidator", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidatePrincipalAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Static": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TValidator", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.ISecurityStampValidator" + ] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ValidationInterval", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ValidationInterval", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnRefreshingPrincipal", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnRefreshingPrincipal", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserManager", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimsFactory", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimsFactory", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Options", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Context", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Context", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserPrincipalAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsSignedIn", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RefreshSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "authenticationMethod", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "authenticationProperties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "authenticationMethod", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignOutAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateSecurityStampAsync", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateTwoFactorSecurityStampAsync", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "securityStamp", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "lockoutOnFailure", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordSignInAsync", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + }, + { + "Name": "password", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "lockoutOnFailure", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CheckPasswordSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + }, + { + "Name": "lockoutOnFailure", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsTwoFactorClientRememberedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RememberTwoFactorClientAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ForgetTwoFactorClientAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TwoFactorRecoveryCodeSignInAsync", + "Parameters": [ + { + "Name": "recoveryCode", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TwoFactorAuthenticatorSignInAsync", + "Parameters": [ + { + "Name": "code", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "rememberClient", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TwoFactorSignInAsync", + "Parameters": [ + { + "Name": "provider", + "Type": "System.String" + }, + { + "Name": "code", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "rememberClient", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorAuthenticationUserAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ExternalLoginSignInAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ExternalLoginSignInAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "bypassTwoFactor", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetExternalAuthenticationSchemesAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetExternalLoginInfoAsync", + "Parameters": [ + { + "Name": "expectedXsrf", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateExternalAuthenticationTokensAsync", + "Parameters": [ + { + "Name": "externalLogin", + "Type": "Microsoft.AspNetCore.Identity.ExternalLoginInfo" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfigureExternalAuthenticationProperties", + "Parameters": [ + { + "Name": "provider", + "Type": "System.String" + }, + { + "Name": "redirectUrl", + "Type": "System.String" + }, + { + "Name": "userId", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignInOrTwoFactorAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "loginProvider", + "Type": "System.String", + "DefaultValue": "null" + }, + { + "Name": "bypassTwoFactor", + "Type": "System.Boolean", + "DefaultValue": "False" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLockedOut", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LockedOut", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PreSignInCheck", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetLockout", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userManager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "contextAccessor", + "Type": "Microsoft.AspNetCore.Http.IHttpContextAccessor" + }, + { + "Name": "claimsFactory", + "Type": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + }, + { + "Name": "schemes", + "Type": "Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.SecurityStampValidator", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ITwoFactorSecurityStampValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "VerifySecurityStamp", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SecurityStampVerified", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "signInManager", + "Type": "Microsoft.AspNetCore.Identity.SignInManager" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/Identity/baseline.netframework.json b/src/Identity/src/Identity/baseline.netframework.json new file mode 100644 index 0000000000..62ab708f0b --- /dev/null +++ b/src/Identity/src/Identity/baseline.netframework.json @@ -0,0 +1,7153 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "System.Security.Claims.PrincipalExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "FindFirstValue", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "claimType", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "setupAction", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RoleClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserNameClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserNameClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserIdClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserIdClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityStampClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityStampClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.DataProtectorTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Protector", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtector", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtectionProvider", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + }, + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Name", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TokenLifespan", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TokenLifespan", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.EmailTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ExternalLoginInfo", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.UserLoginInfo", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Principal", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Principal", + "Parameters": [ + { + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AuthenticationTokens", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AuthenticationTokens", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Services", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TDescriber", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddPasswordValidator", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddUserStore", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleStore", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "T", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProvider", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + }, + { + "Name": "provider", + "Type": "System.Type" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddDefaultTokenProviders", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUserManager", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRoleManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TRoleManager", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "user", + "Type": "System.Type" + }, + { + "Name": "role", + "Type": "System.Type" + }, + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityCookieOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ApplicationCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ApplicationCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExternalCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ExternalCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorRememberMeCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TwoFactorRememberMeCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorUserIdCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TwoFactorUserIdCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ApplicationCookieAuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExternalCookieAuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorUserIdCookieAuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorRememberMeCookieAuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityError", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Code", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Code", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Description", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Description", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "DefaultError", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConcurrencyFailure", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordMismatch", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidToken", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LoginAlreadyAssociated", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidUserName", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateUserName", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InvalidRoleName", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DuplicateRoleName", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserAlreadyHasPassword", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserLockoutNotEnabled", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserAlreadyInRole", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserNotInRole", + "Parameters": [ + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordTooShort", + "Parameters": [ + { + "Name": "length", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresNonAlphanumeric", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresDigit", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresLower", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordRequiresUpper", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityError", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityMarkerService", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Succeeded", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Errors", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Failed", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityError[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Normalize", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "HashPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyHashedPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "hashedPassword", + "Type": "System.String" + }, + { + "Name": "providedPassword", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IPasswordValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "roleId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IRoleValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.ISecurityStampValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "email", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IncrementAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "passwordHash", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HasPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserSecurityStampStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "stamp", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetUserIdAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "userName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedUserName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserTwoFactorStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IUserValidator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.LockoutOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_AllowedForNewUsers", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AllowedForNewUsers", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_MaxFailedAccessAttempts", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaxFailedAccessAttempts", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_DefaultLockoutTimeSpan", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_DefaultLockoutTimeSpan", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasher", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IPasswordHasher" + ], + "Members": [ + { + "Kind": "Method", + "Name": "HashPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyHashedPassword", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "hashedPassword", + "Type": "System.String" + }, + { + "Name": "providedPassword", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordHasher", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "IdentityV2", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "IdentityV3", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordHasherOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_CompatibilityMode", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CompatibilityMode", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IterationCount", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IterationCount", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RequiredLength", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequiredLength", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireNonAlphanumeric", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireNonAlphanumeric", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireLowercase", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireLowercase", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireUppercase", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireUppercase", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireDigit", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireDigit", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IPasswordValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Describer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IPasswordValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsDigit", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLower", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsUpper", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLetterOrDigit", + "Parameters": [ + { + "Name": "c", + "Type": "System.Char" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PasswordVerificationResult", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "Failed", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "Success", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + }, + { + "Kind": "Field", + "Name": "SuccessRehashNeeded", + "Parameters": [], + "GenericParameter": [], + "Literal": "2" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.RoleManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Store", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsQueryableRoles", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsRoleClaims", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RoleExistsAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NormalizeKey", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "roleId", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "name", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [ + { + "Name": "disposing", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IRoleStore" + }, + { + "Name": "roleValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + }, + { + "Name": "contextAccessor", + "Type": "Microsoft.AspNetCore.Http.IHttpContextAccessor" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.RoleValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IRoleValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "role", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ISecurityStampValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.ISecurityStampValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "signInManager", + "Type": "Microsoft.AspNetCore.Identity.SignInManager" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SecurityStampValidator", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ValidatePrincipalAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserManager", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserPrincipalAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsSignedIn", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RefreshSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "authenticationMethod", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "authenticationProperties", + "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + }, + { + "Name": "authenticationMethod", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignOutAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateSecurityStampAsync", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "lockoutOnFailure", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordSignInAsync", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + }, + { + "Name": "password", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "lockoutOnFailure", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsTwoFactorClientRememberedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RememberTwoFactorClientAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ForgetTwoFactorClientAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TwoFactorSignInAsync", + "Parameters": [ + { + "Name": "provider", + "Type": "System.String" + }, + { + "Name": "code", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + }, + { + "Name": "rememberClient", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorAuthenticationUserAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ExternalLoginSignInAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "isPersistent", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetExternalAuthenticationSchemes", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetExternalLoginInfoAsync", + "Parameters": [ + { + "Name": "expectedXsrf", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateExternalAuthenticationTokensAsync", + "Parameters": [ + { + "Name": "externalLogin", + "Type": "Microsoft.AspNetCore.Identity.ExternalLoginInfo" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfigureExternalAuthenticationProperties", + "Parameters": [ + { + "Name": "provider", + "Type": "System.String" + }, + { + "Name": "redirectUrl", + "Type": "System.String" + }, + { + "Name": "userId", + "Type": "System.String", + "DefaultValue": "null" + } + ], + "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userManager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "contextAccessor", + "Type": "Microsoft.AspNetCore.Http.IHttpContextAccessor" + }, + { + "Name": "claimsFactory", + "Type": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RequireConfirmedEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireConfirmedEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireConfirmedPhoneNumber", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireConfirmedPhoneNumber", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.SignInResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Succeeded", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IsLockedOut", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IsLockedOut", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IsNotAllowed", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IsNotAllowed", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequiresTwoFactor", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequiresTwoFactor", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Failed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LockedOut", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NotAllowed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorRequired", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TokenOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProviderMap", + "Parameters": [], + "ReturnType": "System.Collections.Generic.Dictionary", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderMap", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.Dictionary" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EmailConfirmationTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EmailConfirmationTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PasswordResetTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PasswordResetTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ChangeEmailTokenProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ChangeEmailTokenProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultEmailProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultPhoneProvider", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TokenProviderDescriptor", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProviderType", + "Parameters": [], + "ReturnType": "System.Type", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "type", + "Type": "System.Type" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GenerateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserModifierAsync", + "Parameters": [ + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.ILookupNormalizer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Normalize", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.ILookupNormalizer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleManager", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.RoleManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Builder.IdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userManager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "roleManager", + "Type": "Microsoft.AspNetCore.Identity.RoleManager" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserLoginInfo", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_LoginProvider", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LoginProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderKey", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderKey", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderDisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderDisplayName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "System.IDisposable" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Store", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Store", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IUserStore" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Logger", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Logger", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.Extensions.Logging.ILogger" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserAuthenticationTokens", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserTwoFactor", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserPassword", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserSecurityStamp", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserRole", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserLogin", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserPhoneNumber", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserClaim", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsUserLockout", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SupportsQueryableUsers", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserName", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserId", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserAsync", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateConcurrencyStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NormalizeKey", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserIdAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CheckPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HasPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "currentPassword", + "Type": "System.String" + }, + { + "Name": "newPassword", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemovePasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyPasswordAsync", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IUserPasswordStore" + }, + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "password", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GeneratePasswordResetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "newPassword", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roles", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "roles", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateEmailConfirmationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfirmEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateChangeEmailTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "newEmail", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangeEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "newEmail", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateChangePhoneNumberTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyChangePhoneNumberTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "token", + "Type": "System.String" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyUserTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "purpose", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateUserTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "purpose", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RegisterTokenProvider", + "Parameters": [ + { + "Name": "providerName", + "Type": "System.String" + }, + { + "Name": "provider", + "Type": "Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetValidTwoFactorProvidersAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + }, + { + "Name": "token", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateTwoFactorTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "tokenProvider", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsLockedOutAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AccessFailedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + }, + { + "Name": "tokenValue", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveAuthenticationTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "tokenName", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [ + { + "Name": "disposing", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetChangeEmailTokenPurpose", + "Parameters": [ + { + "Name": "newEmail", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "store", + "Type": "Microsoft.AspNetCore.Identity.IUserStore" + }, + { + "Name": "optionsAccessor", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "passwordHasher", + "Type": "Microsoft.AspNetCore.Identity.IPasswordHasher" + }, + { + "Name": "userValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "passwordValidators", + "Type": "System.Collections.Generic.IEnumerable>" + }, + { + "Name": "keyNormalizer", + "Type": "Microsoft.AspNetCore.Identity.ILookupNormalizer" + }, + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + }, + { + "Name": "services", + "Type": "System.IServiceProvider" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger>" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ResetPasswordTokenPurpose", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Protected", + "GenericParameter": [], + "Constant": true, + "Literal": "\"ResetPassword\"" + }, + { + "Kind": "Field", + "Name": "ConfirmEmailTokenPurpose", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Protected", + "GenericParameter": [], + "Constant": true, + "Literal": "\"EmailConfirmation\"" + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_AllowedUserNameCharacters", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AllowedUserNameCharacters", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireUniqueEmail", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireUniqueEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserValidator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserValidator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Describer", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ValidateAsync", + "Parameters": [ + { + "Name": "manager", + "Type": "Microsoft.AspNetCore.Identity.UserManager" + }, + { + "Name": "user", + "Type": "T0" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserValidator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "errors", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "DefaultValue": "null" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Builder.BuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseIdentity", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.IdentityOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ClaimsIdentity", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimsIdentity", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.ClaimsIdentityOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_User", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.UserOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_User", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.UserOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Password", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.PasswordOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Password", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.PasswordOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Lockout", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.LockoutOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Lockout", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.LockoutOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SignIn", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.SignInOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SignIn", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.SignInOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Cookies", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityCookieOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Cookies", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityCookieOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Tokens", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.TokenOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Tokens", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.TokenOptions" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityStampValidationInterval", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityStampValidationInterval", + "Parameters": [ + { + "Name": "value", + "Type": "System.TimeSpan" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ], + "SourceFilters": [] +} \ No newline at end of file diff --git a/src/Identity/src/Specification.Tests/IdentityResultAssert.cs b/src/Identity/src/Specification.Tests/IdentityResultAssert.cs new file mode 100644 index 0000000000..4246d1712f --- /dev/null +++ b/src/Identity/src/Specification.Tests/IdentityResultAssert.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.AspNetCore.Identity.Test +{ + /// + /// Helper for tests to validate identity results. + /// + public static class IdentityResultAssert + { + /// + /// Asserts that the result has Succeeded. + /// + /// + public static void IsSuccess(IdentityResult result) + { + Assert.NotNull(result); + Assert.True(result.Succeeded); + } + + /// + /// Asserts that the result has not Succeeded. + /// + public static void IsFailure(IdentityResult result) + { + Assert.NotNull(result); + Assert.False(result.Succeeded); + } + + /// + /// Asserts that the result has not Succeeded and that error is the first Error's Description. + /// + public static void IsFailure(IdentityResult result, string error) + { + Assert.NotNull(result); + Assert.False(result.Succeeded); + Assert.Equal(error, result.Errors.First().Description); + } + + /// + /// Asserts that the result has not Succeeded and that first error matches error's code and Description. + /// + public static void IsFailure(IdentityResult result, IdentityError error) + { + Assert.NotNull(result); + Assert.False(result.Succeeded); + Assert.Equal(error.Description, result.Errors.First().Description); + Assert.Equal(error.Code, result.Errors.First().Code); + } + + /// + /// Asserts that the logger contains the expectedLog. + /// + /// The logger to inspect. + /// The expected log message. + public static void VerifyLogMessage(ILogger logger, string expectedLog) + { + var testlogger = logger as ITestLogger; + if (testlogger != null) + { + Assert.Contains(expectedLog, testlogger.LogMessages); + } + else + { + Assert.False(true, "No logger registered"); + } + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Specification.Tests/IdentitySpecificationTestBase.cs b/src/Identity/src/Specification.Tests/IdentitySpecificationTestBase.cs new file mode 100644 index 0000000000..9d43420682 --- /dev/null +++ b/src/Identity/src/Specification.Tests/IdentitySpecificationTestBase.cs @@ -0,0 +1,695 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.AspNetCore.Identity.Test +{ + /// + /// Common functionality tests that all verifies user manager functionality regardless of store implementation + /// + /// The type of the user. + /// The type of the role. + public abstract class IdentitySpecificationTestBase : IdentitySpecificationTestBase + where TUser : class + where TRole : class + { } + + /// + /// Base class for tests that exercise basic identity functionality that all stores should support. + /// + /// The type of the user. + /// The type of the role. + /// The primary key type. + public abstract class IdentitySpecificationTestBase : UserManagerSpecificationTestBase + where TUser : class + where TRole : class + where TKey : IEquatable + { + /// + /// Configure the service collection used for tests. + /// + /// + /// + protected override void SetupIdentityServices(IServiceCollection services, object context) + { + services.AddHttpContextAccessor(); + services.AddIdentity(options => + { + options.Password.RequireDigit = false; + options.Password.RequireLowercase = false; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + options.User.AllowedUserNameCharacters = null; + }).AddDefaultTokenProviders(); + AddUserStore(services, context); + AddRoleStore(services, context); + services.AddLogging(); + services.AddSingleton>>(new TestLogger>()); + services.AddSingleton>>(new TestLogger>()); + } + + /// + /// Setup the IdentityBuilder + /// + /// + /// + /// + protected override IdentityBuilder SetupBuilder(IServiceCollection services, object context) + { + var builder = base.SetupBuilder(services, context); + builder.AddRoles(); + AddRoleStore(services, context); + services.AddSingleton>>(new TestLogger>()); + return builder; + } + + /// + /// Creates the role manager for tests. + /// + /// The context that will be passed into the store, typically a db context. + /// The service collection to use, optional. + /// + protected virtual RoleManager CreateRoleManager(object context = null, IServiceCollection services = null) + { + if (services == null) + { + services = new ServiceCollection(); + } + if (context == null) + { + context = CreateTestContext(); + } + SetupIdentityServices(services, context); + return services.BuildServiceProvider().GetService>(); + } + + /// + /// Adds an IRoleStore to services for the test. + /// + /// The service collection to add to. + /// The context for the store to use, optional. + protected abstract void AddRoleStore(IServiceCollection services, object context = null); + + /// + /// Creates a new test role instance. + /// + /// Optional name prefix, name will be randomized. + /// If true, the prefix should be used as the rolename without a random pad. + /// + protected abstract TRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false); + + /// + /// Query used to do name equality checks. + /// + /// The role name to match. + /// The query to use. + protected abstract Expression> RoleNameEqualsPredicate(string roleName); + + /// + /// Query used to do user name prefix matching. + /// + /// The role name to match. + /// The query to use. + protected abstract Expression> RoleNameStartsWithPredicate(string roleName); + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanCreateRoleTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var roleName = "create" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + Assert.False(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(roleName)); + } + + private class AlwaysBadValidator : IUserValidator, IRoleValidator, + IPasswordValidator + { + public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad.", Code = "BadValidator" }; + + public Task ValidateAsync(UserManager manager, TUser user, string password) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(RoleManager manager, TRole role) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(UserManager manager, TUser user) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task BadValidatorBlocksCreateRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + manager.RoleValidators.Clear(); + manager.RoleValidators.Add(new AlwaysBadValidator()); + var role = CreateTestRole("blocked"); + IdentityResultAssert.IsFailure(await manager.CreateAsync(role), + AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Role {await manager.GetRoleIdAsync(role) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChainRoleValidators() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + manager.RoleValidators.Clear(); + manager.RoleValidators.Add(new AlwaysBadValidator()); + manager.RoleValidators.Add(new AlwaysBadValidator()); + var role = CreateTestRole("blocked"); + var result = await manager.CreateAsync(role); + IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Role {await manager.GetRoleIdAsync(role) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code};{AlwaysBadValidator.ErrorMessage.Code}."); + Assert.Equal(2, result.Errors.Count()); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task BadValidatorBlocksRoleUpdate() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var role = CreateTestRole("poorguy"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + var error = AlwaysBadValidator.ErrorMessage; + manager.RoleValidators.Clear(); + manager.RoleValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(role), error); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Role {await manager.GetRoleIdAsync(role) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanDeleteRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var roleName = "delete" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + Assert.False(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(role)); + Assert.False(await manager.RoleExistsAsync(roleName)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanAddRemoveRoleClaim() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var role = CreateTestRole("ClaimsAddRemove"); + var roleSafe = CreateTestRole("ClaimsAdd"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(roleSafe)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (Claim c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(role, c)); + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(roleSafe, c)); + } + var roleClaims = await manager.GetClaimsAsync(role); + var safeRoleClaims = await manager.GetClaimsAsync(roleSafe); + Assert.Equal(3, roleClaims.Count); + Assert.Equal(3, safeRoleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[0])); + roleClaims = await manager.GetClaimsAsync(role); + safeRoleClaims = await manager.GetClaimsAsync(roleSafe); + Assert.Equal(2, roleClaims.Count); + Assert.Equal(3, safeRoleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[1])); + roleClaims = await manager.GetClaimsAsync(role); + safeRoleClaims = await manager.GetClaimsAsync(roleSafe); + Assert.Equal(1, roleClaims.Count); + Assert.Equal(3, safeRoleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[2])); + roleClaims = await manager.GetClaimsAsync(role); + safeRoleClaims = await manager.GetClaimsAsync(roleSafe); + Assert.Equal(0, roleClaims.Count); + Assert.Equal(3, safeRoleClaims.Count); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanRoleFindById() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var role = CreateTestRole("FindByIdAsync"); + Assert.Null(await manager.FindByIdAsync(await manager.GetRoleIdAsync(role))); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByIdAsync(await manager.GetRoleIdAsync(role))); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanRoleFindByName() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var roleName = "FindByNameAsync" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + Assert.Null(await manager.FindByNameAsync(roleName)); + Assert.False(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByNameAsync(roleName)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanUpdateRoleName() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var roleName = "update" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + Assert.False(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.SetRoleNameAsync(role, "Changed")); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role)); + Assert.False(await manager.RoleExistsAsync("update")); + Assert.Equal(role, await manager.FindByNameAsync("Changed")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanQueryableRoles() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + if (manager.SupportsQueryableRoles) + { + var roles = GenerateRoles("CanQuerableRolesTest", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(r)); + } + Expression> func = RoleNameStartsWithPredicate("CanQuerableRolesTest"); + Assert.Equal(roles.Count, manager.Roles.Count(func)); + func = RoleNameEqualsPredicate("bogus"); + Assert.Null(manager.Roles.FirstOrDefault(func)); + + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CreateRoleFailsIfExists() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateRoleManager(); + var roleName = "dupeRole" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + Assert.False(await manager.RoleExistsAsync(roleName)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(roleName)); + var role2 = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + IdentityResultAssert.IsFailure(await manager.CreateAsync(role2)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanAddUsersToRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var manager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var roleName = "AddUserTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role)); + TUser[] users = + { + CreateTestUser("1"),CreateTestUser("2"),CreateTestUser("3"),CreateTestUser("4"), + }; + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(u, roleName)); + Assert.True(await manager.IsInRoleAsync(u, roleName)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGetRolesForUser() + { + if (ShouldSkipDbTests()) + { + return; + } + + var context = CreateTestContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanGetRolesForUser", 4); + var roles = GenerateRoles("CanGetRolesForUserRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, await roleManager.GetRoleNameAsync(r))); + Assert.True(await userManager.IsInRoleAsync(u, await roleManager.GetRoleNameAsync(r))); + } + } + + foreach (var u in users) + { + var rs = await userManager.GetRolesAsync(u); + Assert.Equal(roles.Count, rs.Count); + foreach (var r in roles) + { + var expectedRoleName = await roleManager.GetRoleNameAsync(r); + Assert.Contains(rs, role => role == expectedRoleName); + } + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task RemoveUserFromRoleWithMultipleRoles() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user)); + var roles = GenerateRoles("RemoveUserFromRoleWithMultipleRoles", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, await roleManager.GetRoleNameAsync(r))); + Assert.True(await userManager.IsInRoleAsync(user, await roleManager.GetRoleNameAsync(r))); + } + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(user, await roleManager.GetRoleNameAsync(roles[2]))); + Assert.False(await userManager.IsInRoleAsync(user, await roleManager.GetRoleNameAsync(roles[2]))); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanRemoveUsersFromRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanRemoveUsersFromRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + var r = CreateTestRole("r1"); + var roleName = await roleManager.GetRoleNameAsync(r); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, roleName)); + Assert.True(await userManager.IsInRoleAsync(u, roleName)); + } + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(u, roleName)); + Assert.False(await userManager.IsInRoleAsync(u, roleName)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task RemoveUserNotInRoleFails() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var roleName = "addUserDupeTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var result = await userMgr.RemoveFromRoleAsync(user, roleName); + IdentityResultAssert.IsFailure(result, _errorDescriber.UserNotInRole(roleName)); + IdentityResultAssert.VerifyLogMessage(userMgr.Logger, $"User {await userMgr.GetUserIdAsync(user)} is not in role {roleName}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddUserToRoleFailsIfAlreadyInRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var roleName = "addUserDupeTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, roleName)); + Assert.True(await userMgr.IsInRoleAsync(user, roleName)); + IdentityResultAssert.IsFailure(await userMgr.AddToRoleAsync(user, roleName), _errorDescriber.UserAlreadyInRole(roleName)); + IdentityResultAssert.VerifyLogMessage(userMgr.Logger, $"User {await userMgr.GetUserIdAsync(user)} is already in role {roleName}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddUserToRolesIgnoresDuplicates() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var roleName = "addUserDupeTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.False(await userMgr.IsInRoleAsync(user, roleName)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRolesAsync(user, new[] { roleName, roleName })); + Assert.True(await userMgr.IsInRoleAsync(user, roleName)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanFindRoleByNameWithManager() + { + if (ShouldSkipDbTests()) + { + return; + } + var roleMgr = CreateRoleManager(); + var roleName = "findRoleByNameTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.NotNull(await roleMgr.FindByNameAsync(roleName)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanFindRoleWithManager() + { + if (ShouldSkipDbTests()) + { + return; + } + var roleMgr = CreateRoleManager(); + var roleName = "findRoleTest" + Guid.NewGuid().ToString(); + var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(roleName, await roleMgr.GetRoleNameAsync(await roleMgr.FindByNameAsync(roleName))); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGetUsersInRole() + { + if (ShouldSkipDbTests()) + { + return; + } + var context = CreateTestContext(); + var manager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var roles = GenerateRoles("UsersInRole", 4); + var roleNameList = new List(); + + foreach (var role in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role)); + roleNameList.Add(await roleManager.GetRoleNameAsync(role)); + } + + for (int i = 0; i < 6; i++) + { + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + + if ((i % 2) == 0) + { + IdentityResultAssert.IsSuccess(await manager.AddToRolesAsync(user, roleNameList)); + } + } + + foreach (var role in roles) + { + Assert.Equal(3, (await manager.GetUsersInRoleAsync(await roleManager.GetRoleNameAsync(role))).Count); + } + + Assert.Equal(0, (await manager.GetUsersInRoleAsync("123456")).Count); + } + + private List GenerateRoles(string namePrefix, int count) + { + var roles = new List(count); + for (var i = 0; i < count; i++) + { + roles.Add(CreateTestRole(namePrefix + i)); + } + return roles; + } + } +} diff --git a/src/Identity/src/Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.csproj b/src/Identity/src/Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.csproj new file mode 100644 index 0000000000..73155b6e2d --- /dev/null +++ b/src/Identity/src/Specification.Tests/Microsoft.AspNetCore.Identity.Specification.Tests.csproj @@ -0,0 +1,23 @@ + + + + Shared test suite for Asp.Net Identity Core store implementations. + netstandard2.0 + true + aspnetcore;identity;membership + true + false + + + + + + + + + + + + + + diff --git a/src/Identity/src/Specification.Tests/TestLogger.cs b/src/Identity/src/Specification.Tests/TestLogger.cs new file mode 100644 index 0000000000..7b9ebdece8 --- /dev/null +++ b/src/Identity/src/Specification.Tests/TestLogger.cs @@ -0,0 +1,74 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.Test +{ + /// + /// test logger. + /// + public interface ITestLogger + { + /// + /// log messages. + /// + IList LogMessages { get; } + } + + /// + /// Test logger. + /// + /// + public class TestLogger : ILogger, ITestLogger + { + /// + /// log messages. + /// + public IList LogMessages { get; } = new List(); + + /// + /// + /// + /// + /// + public IDisposable BeginScope(TState state) + { + LogMessages.Add(state?.ToString()); + return null; + } + + /// + /// + /// + /// + /// + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (formatter == null) + { + LogMessages.Add(state.ToString()); + } + else + { + LogMessages.Add(formatter(state, exception)); + } + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Specification.Tests/UserManagerSpecificationTests.cs b/src/Identity/src/Specification.Tests/UserManagerSpecificationTests.cs new file mode 100644 index 0000000000..ac8bc417b8 --- /dev/null +++ b/src/Identity/src/Specification.Tests/UserManagerSpecificationTests.cs @@ -0,0 +1,2191 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.AspNetCore.Identity.Test +{ + /// + /// Base class for tests that exercise basic identity functionality that all stores should support. + /// + /// The type of the user. + public abstract class UserManagerSpecificationTestBase : UserManagerSpecificationTestBase where TUser : class { } + + /// + /// Base class for tests that exercise basic identity functionality that all stores should support. + /// + /// The type of the user. + /// The primary key type. + public abstract class UserManagerSpecificationTestBase + where TUser : class + where TKey : IEquatable + { + /// + /// Null value. + /// + protected const string NullValue = "(null)"; + + /// + /// Error describer. + /// + protected readonly IdentityErrorDescriber _errorDescriber = new IdentityErrorDescriber(); + + /// + /// Configure the service collection used for tests. + /// + /// + /// + protected virtual void SetupIdentityServices(IServiceCollection services, object context) + => SetupBuilder(services, context); + + /// + /// Configure the service collection used for tests. + /// + /// + /// + protected virtual IdentityBuilder SetupBuilder(IServiceCollection services, object context) + { + services.AddHttpContextAccessor(); + services.AddDataProtection(); + var builder = services.AddIdentityCore(options => + { + options.Password.RequireDigit = false; + options.Password.RequireLowercase = false; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + options.User.AllowedUserNameCharacters = null; + }).AddDefaultTokenProviders(); + AddUserStore(services, context); + services.AddLogging(); + services.AddSingleton>>(new TestLogger>()); + return builder; + } + + /// + /// If true, tests that require a database will be skipped. + /// + /// + protected virtual bool ShouldSkipDbTests() => false; + + /// + /// Creates the user manager used for tests. + /// + /// The context that will be passed into the store, typically a db context. + /// The service collection to use, optional. + /// Delegate used to configure the services, optional. + /// The user manager to use for tests. + protected virtual UserManager CreateManager(object context = null, IServiceCollection services = null, Action configureServices = null) + { + if (services == null) + { + services = new ServiceCollection(); + } + if (context == null) + { + context = CreateTestContext(); + } + SetupIdentityServices(services, context); + configureServices?.Invoke(services); + return services.BuildServiceProvider().GetService>(); + } + + /// + /// Creates the context object for a test, typically a DbContext. + /// + /// The context object for a test, typically a DbContext. + protected abstract object CreateTestContext(); + + /// + /// Adds an IUserStore to services for the test. + /// + /// The service collection to add to. + /// The context for the store to use, optional. + protected abstract void AddUserStore(IServiceCollection services, object context = null); + + /// + /// Set the user's password hash. + /// + /// The user to set. + /// The password hash to set. + protected abstract void SetUserPasswordHash(TUser user, string hashedPassword); + + /// + /// Create a new test user instance. + /// + /// Optional name prefix, name will be randomized. + /// Optional email. + /// Optional phone number. + /// Optional lockout enabled. + /// Optional lockout end. + /// If true, the prefix should be used as the username without a random pad. + /// The new test user instance. + protected abstract TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "", + bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = null, bool useNamePrefixAsUserName = false); + + /// + /// Query used to do name equality checks. + /// + /// The user name to match. + /// The query to use. + protected abstract Expression> UserNameEqualsPredicate(string userName); + + /// + /// Query used to do user name prefix matching. + /// + /// The user name to match. + /// The query to use. + protected abstract Expression> UserNameStartsWithPredicate(string userName); + + private class AlwaysBadValidator : IUserValidator, + IPasswordValidator + { + public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad.", Code = "BadValidator" }; + + public Task ValidateAsync(UserManager manager, TUser user, string password) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(UserManager manager, TUser user) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CreateUserWillSetCreateDateOnlyIfSupported() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var userId = await manager.GetUserIdAsync(user); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanDeleteUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var userId = await manager.GetUserIdAsync(user); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + Assert.Null(await manager.FindByIdAsync(userId)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanUpdateUserName() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var name = Guid.NewGuid().ToString(); + var user = CreateTestUser(name); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var newName = Guid.NewGuid().ToString(); + Assert.Null(await manager.FindByNameAsync(newName)); + IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName)); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.NotNull(await manager.FindByNameAsync(newName)); + Assert.Null(await manager.FindByNameAsync(name)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CheckSetUserNameValidatesUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "UpdateAsync" + Guid.NewGuid().ToString(); + var newUsername = "New" + Guid.NewGuid().ToString(); + var user = CreateTestUser(username, useNamePrefixAsUserName: true); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.Null(await manager.FindByNameAsync(newUsername)); + IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername)); + Assert.NotNull(await manager.FindByNameAsync(newUsername)); + Assert.Null(await manager.FindByNameAsync(username)); + + var newUser = CreateTestUser(username, useNamePrefixAsUserName: true); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser)); + var error = _errorDescriber.InvalidUserName(""); + IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, ""), error); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}."); + + error = _errorDescriber.DuplicateUserName(newUsername); + IdentityResultAssert.IsFailure(await manager.SetUserNameAsync(newUser, newUsername), error); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(newUser)} validation failed: {error.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task SetUserNameUpdatesSecurityStamp() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "UpdateAsync" + Guid.NewGuid().ToString(); + var newUsername = "New" + Guid.NewGuid().ToString(); + var user = CreateTestUser(username, useNamePrefixAsUserName: true); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.Null(await manager.FindByNameAsync(newUsername)); + IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newUsername)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CreateUpdatesSecurityStamp() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "Create" + Guid.NewGuid().ToString(); + var user = CreateTestUser(username, useNamePrefixAsUserName: true); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ResetAuthenticatorKeyUpdatesSecurityStamp() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "Create" + Guid.NewGuid().ToString(); + var user = CreateTestUser(username, useNamePrefixAsUserName: true); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CheckSetEmailValidatesUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.Options.User.RequireUniqueEmail = true; + manager.UserValidators.Add(new UserValidator()); + var random = new Random(); + var email = "foo" + random.Next() + "@example.com"; + var newEmail = "bar" + random.Next() + "@example.com"; + var user = CreateTestUser(email: email); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, newEmail)); + + var newUser = CreateTestUser(email: email); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(newUser)); + IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, newEmail), _errorDescriber.DuplicateEmail(newEmail)); + IdentityResultAssert.IsFailure(await manager.SetEmailAsync(newUser, ""), _errorDescriber.InvalidEmail("")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanUpdatePasswordUsingHasher() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("UpdatePassword"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "password")); + var userId = await manager.GetUserIdAsync(user); + + SetUserPasswordHash(user, manager.PasswordHasher.HashPassword(user, "New")); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.False(await manager.CheckPasswordAsync(user, "password")); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Invalid password for user {await manager.GetUserIdAsync(user)}."); + Assert.True(await manager.CheckPasswordAsync(user, "New")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanFindById() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.NotNull(await manager.FindByIdAsync(await manager.GetUserIdAsync(user))); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task UserValidatorCanBlockCreate() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + manager.UserValidators.Clear(); + manager.UserValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task UserValidatorCanBlockUpdate() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.UserValidators.Clear(); + manager.UserValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChainUserValidators() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.UserValidators.Clear(); + var user = CreateTestUser(); + manager.UserValidators.Add(new AlwaysBadValidator()); + manager.UserValidators.Add(new AlwaysBadValidator()); + var result = await manager.CreateAsync(user); + IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} validation failed: {AlwaysBadValidator.ErrorMessage.Code};{AlwaysBadValidator.ErrorMessage.Code}."); + Assert.Equal(2, result.Errors.Count()); + } + + /// + /// Test. + /// + /// Task + [Theory] + [InlineData("")] + [InlineData(null)] + public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email)); + } + + /// + /// Test. + /// + /// Task + [Theory] + [InlineData("@@afd")] + [InlineData("bogus")] + public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("UpdateBlocked", email); + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), _errorDescriber.InvalidEmail(email)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task PasswordValidatorCanBlockAddPassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.PasswordValidators.Clear(); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChainPasswordValidators() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.PasswordValidators.Clear(); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var result = await manager.AddPasswordAsync(user, "pwd"); + IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage); + Assert.Equal(2, result.Errors.Count()); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task PasswordValidatorCanBlockChangePassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + manager.PasswordValidators.Clear(); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"), + AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task PasswordValidatorCanBlockCreateUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + manager.PasswordValidators.Clear(); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user, "password"), AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user) ?? NullValue} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanCreateUserNoPassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "CreateUserTest" + Guid.NewGuid(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(CreateTestUser(username, useNamePrefixAsUserName: true))); + var user = await manager.FindByNameAsync(username); + Assert.NotNull(user); + Assert.False(await manager.HasPasswordAsync(user)); + Assert.False(await manager.CheckPasswordAsync(user, "whatever")); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Empty(logins); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanCreateUserAddLogin() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + const string provider = "ZzAuth"; + const string display = "display"; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var providerKey = await manager.GetUserIdAsync(user); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo(provider, providerKey, display))); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Single(logins); + Assert.Equal(provider, logins.First().LoginProvider); + Assert.Equal(providerKey, logins.First().ProviderKey); + Assert.Equal(display, logins.First().ProviderDisplayName); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanCreateUserLoginAndAddPassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var userId = await manager.GetUserIdAsync(user); + var login = new UserLoginInfo("Provider", userId, "display"); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.False(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password")); + Assert.True(await manager.HasPasswordAsync(user)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Single(logins); + Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey)); + Assert.True(await manager.CheckPasswordAsync(user, "password")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddPasswordFailsIfAlreadyHave() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); + Assert.True(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + "User already has a password set."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} already has a password."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanCreateUserAddRemoveLogin() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + var result = await manager.CreateAsync(user); + Assert.NotNull(user); + var userId = await manager.GetUserIdAsync(user); + var login = new UserLoginInfo("Provider", userId, "display"); + IdentityResultAssert.IsSuccess(result); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Single(logins); + Assert.Equal(login.LoginProvider, logins.Last().LoginProvider); + Assert.Equal(login.ProviderKey, logins.Last().ProviderKey); + Assert.Equal(login.ProviderDisplayName, logins.Last().ProviderDisplayName); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey)); + Assert.Null(await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey)); + logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Empty(logins); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanRemovePassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("CanRemovePassword"); + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = await manager.GetSecurityStampAsync(user); + var username = await manager.GetUserNameAsync(user); + IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user)); + var u = await manager.FindByNameAsync(username); + Assert.NotNull(u); + Assert.False(await manager.HasPasswordAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChangePassword() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword)); + Assert.False(await manager.CheckPasswordAsync(user, password)); + Assert.True(await manager.CheckPasswordAsync(user, newPassword)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanAddRemoveUserClaim() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (Claim c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + } + var userId = await manager.GetUserIdAsync(user); + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(3, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(2, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(0, userClaims.Count); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task RemoveClaimOnlyAffectsUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + var user2 = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (Claim c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c)); + } + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(3, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(2, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(0, userClaims.Count); + var userClaims2 = await manager.GetClaimsAsync(user2); + Assert.Equal(3, userClaims2.Count); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanReplaceUserClaim() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a"))); + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + Claim claim = new Claim("c", "b"); + Claim oldClaim = userClaims.FirstOrDefault(); + IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim)); + var newUserClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, newUserClaims.Count); + Claim newClaim = newUserClaims.FirstOrDefault(); + Assert.Equal(claim.Type, newClaim.Type); + Assert.Equal(claim.Value, newClaim.Value); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ReplaceUserClaimOnlyAffectsUser() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + var user2 = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a"))); + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, new Claim("c", "a"))); + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + var userClaims2 = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims2.Count); + Claim claim = new Claim("c", "b"); + Claim oldClaim = userClaims.FirstOrDefault(); + IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim)); + var newUserClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, newUserClaims.Count); + Claim newClaim = newUserClaims.FirstOrDefault(); + Assert.Equal(claim.Type, newClaim.Type); + Assert.Equal(claim.Value, newClaim.Value); + userClaims2 = await manager.GetClaimsAsync(user2); + Assert.Equal(1, userClaims2.Count); + Claim oldClaim2 = userClaims2.FirstOrDefault(); + Assert.Equal("c", oldClaim2.Type); + Assert.Equal("a", oldClaim2.Value); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangePasswordFallsIfPasswordWrong() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword"); + IdentityResultAssert.IsFailure(result, "Incorrect password."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Change password failed for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddDupeUserNameFails() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var username = "AddDupeUserNameFails" + Guid.NewGuid(); + var user = CreateTestUser(username, useNamePrefixAsUserName: true); + var user2 = CreateTestUser(username, useNamePrefixAsUserName: true); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateUserName(username)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddDupeEmailAllowedByDefault() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(email: "yup@yup.com"); + var user2 = CreateTestUser(email: "yup@yup.com"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user2, await manager.GetEmailAsync(user))); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddDupeEmailFailsWhenUniqueEmailRequired() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.Options.User.RequireUniqueEmail = true; + var user = CreateTestUser(email: "FooUser@yup.com"); + var user2 = CreateTestUser(email: "FooUser@yup.com"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), _errorDescriber.DuplicateEmail("FooUser@yup.com")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task UpdateSecurityStampActuallyChanges() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + Assert.Null(await manager.GetSecurityStampAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task AddDupeLoginFails() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + var login = new UserLoginInfo("Provider", "key", "display"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var result = await manager.AddLoginAsync(user, login); + IdentityResultAssert.IsFailure(result, _errorDescriber.LoginAlreadyAssociated()); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"AddLogin for user {await manager.GetUserIdAsync(user)} failed because it was already associated with another user."); + } + + // Email tests + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanFindByEmail() + { + if (ShouldSkipDbTests()) + { + return; + } + var email = "foouser@test.com"; + var manager = CreateManager(); + var user = CreateTestUser(email: email); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var fetch = await manager.FindByEmailAsync(email); + Assert.Equal(user, fetch); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async virtual Task CanFindUsersViaUserQuerable() + { + if (ShouldSkipDbTests()) + { + return; + } + + var mgr = CreateManager(); + if (mgr.SupportsQueryableUsers) + { + var users = GenerateUsers("CanFindUsersViaUserQuerable", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u)); + } + Assert.Equal(users.Count, mgr.Users.Count(UserNameStartsWithPredicate("CanFindUsersViaUserQuerable"))); + Assert.Null(mgr.Users.FirstOrDefault(UserNameEqualsPredicate("bogus"))); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ConfirmEmailFalseByDefaultTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + private class StaticTokenProvider : IUserTwoFactorTokenProvider + { + public async Task GenerateAsync(string purpose, UserManager manager, TUser user) + { + return MakeToken(purpose, await manager.GetUserIdAsync(user)); + } + + public async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + { + return token == MakeToken(purpose, await manager.GetUserIdAsync(user)); + } + + public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + { + return Task.FromResult(true); + } + + private static string MakeToken(string purpose, string userId) + { + return string.Join(":", userId, purpose, "ImmaToken"); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanResetPasswordWithStaticTokenProvider() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + manager.Options.Tokens.PasswordResetTokenProvider = "Static"; + var user = CreateTestUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + var userId = await manager.GetUserIdAsync(user); + IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword)); + Assert.False(await manager.CheckPasswordAsync(user, password)); + Assert.True(await manager.CheckPasswordAsync(user, newPassword)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + manager.Options.Tokens.PasswordResetTokenProvider = "Static"; + var user = CreateTestUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + manager.PasswordValidators.Add(new AlwaysBadValidator()); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword), + AlwaysBadValidator.ErrorMessage); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}."); + Assert.True(await manager.CheckPasswordAsync(user, password)); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + manager.Options.Tokens.PasswordResetTokenProvider = "Static"; + var user = CreateTestUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ResetPassword for user { await manager.GetUserIdAsync(user)}."); + Assert.True(await manager.CheckPasswordAsync(user, password)); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + var user = CreateTestUser(); + var user2 = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + var userId = await manager.GetUserIdAsync(user); + var token = await manager.GenerateUserTokenAsync(user, "Static", "test"); + + Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token)); + + Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test2 for user { await manager.GetUserIdAsync(user)}."); + + Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a")); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user)}."); + + Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: test for user { await manager.GetUserIdAsync(user2)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanConfirmEmailWithStaticToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + manager.Options.Tokens.EmailConfirmationTokenProvider = "Static"; + var user = CreateTestUser(); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + Assert.NotNull(token); + var userId = await manager.GetUserIdAsync(user); + IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Static", new StaticTokenProvider()); + manager.Options.Tokens.EmailConfirmationTokenProvider = "Static"; + var user = CreateTestUser(); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ConfirmTokenFailsAfterPasswordChange() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(namePrefix: "Test"); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword")); + IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user { await manager.GetUserIdAsync(user)}."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // Lockout tests + + /// + /// Test. + /// + /// Task + [Fact] + public async Task SingleFailureLockout() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 0; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out."); + + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task TwoFailureLockout() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out."); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ResetAccessCountPreventsLockout() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanEnableLockoutManuallyAndLockout() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.AllowedForNewUsers = false; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"User {await mgr.GetUserIdAsync(user)} is locked out."); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset())); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task LockoutFailsIfNotEnabled() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + mgr.Options.Lockout.AllowedForNewUsers = false; + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()), + "Lockout is not enabled for this user."); + IdentityResultAssert.VerifyLogMessage(mgr.Logger, $"Lockout for user {await mgr.GetUserIdAsync(user)} failed because lockout is not enabled for this user."); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + var user = CreateTestUser(lockoutEnd: DateTimeOffset.UtcNow.AddSeconds(-1)); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1))); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + var lockoutEnd = DateTimeOffset.UtcNow.AddMinutes(5); + var user = CreateTestUser(lockoutEnd: lockoutEnd); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() + { + if (ShouldSkipDbTests()) + { + return; + } + var mgr = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime()); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd)); + Assert.True(await mgr.IsLockedOutAsync(user)); + var end = await mgr.GetLockoutEndDateAsync(user); + Assert.Equal(lockoutEnd, end); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task SetPhoneNumberTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChangePhoneNumber() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1)); + Assert.True(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangePhoneNumberTokenIsInt() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + Assert.True(int.TryParse(token1, out var ignored)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangePhoneNumberFailsWithWrongToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"), + "Invalid token."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-111-1111 for user {await manager.GetUserIdAsync(user)}."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user)); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + private class YesPhoneNumberProvider : IUserTwoFactorTokenProvider + { + public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TUser user) + => Task.FromResult(true); + + public Task GenerateAsync(string purpose, UserManager manager, TUser user) + => Task.FromResult(purpose); + + public Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + => Task.FromResult(true); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangePhoneNumberWithCustomProvider() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + manager.RegisterTokenProvider("Yes", new YesPhoneNumberProvider()); + manager.Options.Tokens.ChangePhoneNumberTokenProvider = "Yes"; + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "whatever")); + Assert.True(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangePhoneNumberFailsWithWrongPhoneNumber() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "bogus", token1), + "Invalid token."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user)); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanVerifyPhoneNumber() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string num1 = "111-123-4567"; + const string num2 = "111-111-1111"; + var userId = await manager.GetUserIdAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1); + + var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2); + Assert.NotEqual(token1, token2); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1)); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:{num1} for user {await manager.GetUserIdAsync(user)}."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:{num2} for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChangeEmail() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var newEmail = await manager.GetUserNameAsync(user) + "@en.vec"; + var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail); + IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), newEmail); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanChangeEmailWithDifferentTokenProvider() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(context: null, services: null, + configureServices: s => s.Configure( + o => o.Tokens.ProviderMap["NewProvider2"] = new TokenProviderDescriptor(typeof(EmailTokenProvider)))); + manager.Options.Tokens.ChangeEmailTokenProvider = "NewProvider2"; + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var newEmail = await manager.GetUserNameAsync(user) + "@en.vec"; + var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail); + IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), newEmail); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangeEmailTokensFailsAfterEmailChanged() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var newEmail = await manager.GetUserNameAsync(user) + "@en.vec"; + var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "another@email.com")); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, newEmail, token1)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal("another@email.com", await manager.GetEmailAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangeEmailFailsWithWrongToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + string oldEmail = email; + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "whatevah@foo.boop", "bogus"), + "Invalid token."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:whatevah@foo.boop for user { await manager.GetUserIdAsync(user)}."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), oldEmail); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task ChangeEmailFailsWithEmail() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + string oldEmail = email; + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangeEmailTokenAsync(user, "forgot@alrea.dy"); + IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "oops@foo.boop", token1), + "Invalid token."); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangeEmail:oops@foo.boop for user { await manager.GetUserIdAsync(user)}."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), oldEmail); + Assert.Equal(stamp, await manager.GetSecurityStampAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task EmailFactorFailsAfterSecurityStampChangeTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + string factorId = "Email"; //default + var user = CreateTestUser("foouser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var email = await manager.GetUserNameAsync(user) + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, email)); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + await manager.ConfirmEmailAsync(user, token); + + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task EnableTwoFactorChangesSecurityStamp() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + Assert.True(await manager.GetTwoFactorEnabledAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var error = $"No IUserTwoFactorTokenProvider<{nameof(TUser)}> named 'bogus' is registered."; + var ex = await Assert.ThrowsAsync( + () => manager.GenerateTwoFactorTokenAsync(user, "bogus")); + Assert.Equal(error, ex.Message); + ex = await Assert.ThrowsAsync( + () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus")); + Assert.Equal(error, ex.Message); + ex = await Assert.ThrowsAsync( + () => manager.VerifyUserTokenAsync(user, "bogus", "bogus", "bogus")); + Assert.Equal(error, ex.Message); + ex = await Assert.ThrowsAsync( + () => manager.GenerateUserTokenAsync(user, "bogus", "bogus")); + Assert.Equal(error, ex.Message); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task GetValidTwoFactorTestEmptyWithNoProviders() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGetSetUpdateAndRemoveUserToken() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name")); + IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value")); + Assert.Equal("value", await manager.GetAuthenticationTokenAsync(user, "provider", "name")); + + IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "provider", "name", "value2")); + Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name")); + + IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "whatevs", "name")); + Assert.Equal("value2", await manager.GetAuthenticationTokenAsync(user, "provider", "name")); + + IdentityResultAssert.IsSuccess(await manager.RemoveAuthenticationTokenAsync(user, "provider", "name")); + Assert.Null(await manager.GetAuthenticationTokenAsync(user, "provider", "name")); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanRedeemRecoveryCodeOnlyOnce() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + + var numCodes = 15; + var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes); + Assert.Equal(numCodes, newCodes.Count()); + + foreach (var code in newCodes) + { + IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code)); + IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code)); + Assert.Equal(--numCodes, await manager.CountRecoveryCodesAsync(user)); + } + // One last time to be sure + foreach (var code in newCodes) + { + IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task RecoveryCodesInvalidAfterReplace() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + + var numCodes = 15; + var newCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes); + Assert.Equal(numCodes, newCodes.Count()); + var realCodes = await manager.GenerateNewTwoFactorRecoveryCodesAsync(user, numCodes); + Assert.Equal(numCodes, realCodes.Count()); + + foreach (var code in newCodes) + { + IdentityResultAssert.IsFailure(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code)); + } + + foreach (var code in realCodes) + { + IdentityResultAssert.IsSuccess(await manager.RedeemTwoFactorRecoveryCodeAsync(user, code)); + } + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGetValidTwoFactor() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var userId = await manager.GetUserIdAsync(user); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.False(factors.Any()); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + var token = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token)); + await manager.UpdateAsync(user); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.Single(factors); + Assert.Equal("Phone", factors[0]); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com")); + token = await manager.GenerateEmailConfirmationTokenAsync(user); + await manager.ConfirmEmailAsync(user, token); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.Equal(2, factors.Count()); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.Single(factors); + Assert.Equal("Phone", factors[0]); + IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user)); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.Equal(2, factors.Count()); + Assert.Equal("Authenticator", factors[1]); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task PhoneFactorFailsAfterSecurityStampChangeTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var factorId = "Phone"; // default + var user = CreateTestUser(phoneNumber: "4251234567"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task VerifyTokenFromWrongTokenProviderFails() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "4251234567"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateTwoFactorTokenAsync(user, "Phone"); + Assert.NotNull(token); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Email", token)); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task VerifyWithWrongSmsTokenFails() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + var user = CreateTestUser(phoneNumber: "4251234567"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Phone", "bogus")); + IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyTwoFactorTokenAsync() failed for user {await manager.GetUserIdAsync(user)}."); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task NullableDateTimeOperationTest() + { + if (ShouldSkipDbTests()) + { + return; + } + var userMgr = CreateManager(); + var user = CreateTestUser(lockoutEnabled: true); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + + Assert.Null(await userMgr.GetLockoutEndDateAsync(user)); + + // set LockoutDateEndDate to null + await userMgr.SetLockoutEndDateAsync(user, null); + Assert.Null(await userMgr.GetLockoutEndDateAsync(user)); + + // set to a valid value + await userMgr.SetLockoutEndDateAsync(user, DateTimeOffset.Parse("01/01/2014")); + Assert.Equal(DateTimeOffset.Parse("01/01/2014"), await userMgr.GetLockoutEndDateAsync(user)); + } + + /// + /// Test. + /// + /// Task + [Fact] + public async Task CanGetUsersWithClaims() + { + if (ShouldSkipDbTests()) + { + return; + } + var manager = CreateManager(); + + for (int i = 0; i < 6; i++) + { + var user = CreateTestUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + + if ((i % 2) == 0) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("foo", "bar"))); + } + } + + Assert.Equal(3, (await manager.GetUsersForClaimAsync(new Claim("foo", "bar"))).Count); + + Assert.Equal(0, (await manager.GetUsersForClaimAsync(new Claim("123", "456"))).Count); + } + + /// + /// Generate count users with a name prefix. + /// + /// + /// + /// + protected List GenerateUsers(string userNamePrefix, int count) + { + var users = new List(count); + for (var i = 0; i < count; i++) + { + users.Add(CreateTestUser(userNamePrefix + i)); + } + return users; + } + } +} diff --git a/src/Identity/src/Specification.Tests/baseline.netcore.json b/src/Identity/src/Specification.Tests/baseline.netcore.json new file mode 100644 index 0000000000..f8a2885880 --- /dev/null +++ b/src/Identity/src/Specification.Tests/baseline.netcore.json @@ -0,0 +1,1536 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Identity.Specification.Tests, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.Identity.Test.IdentityResultAssert", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "IsSuccess", + "Parameters": [ + { + "Name": "result", + "Type": "Microsoft.AspNetCore.Identity.IdentityResult" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsFailure", + "Parameters": [ + { + "Name": "result", + "Type": "Microsoft.AspNetCore.Identity.IdentityResult" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsFailure", + "Parameters": [ + { + "Name": "result", + "Type": "Microsoft.AspNetCore.Identity.IdentityResult" + }, + { + "Name": "error", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsFailure", + "Parameters": [ + { + "Name": "result", + "Type": "Microsoft.AspNetCore.Identity.IdentityResult" + }, + { + "Name": "error", + "Type": "Microsoft.AspNetCore.Identity.IdentityError" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyLogMessage", + "Parameters": [ + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILogger" + }, + { + "Name": "expectedLog", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.IdentitySpecificationTestBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Identity.Test.IdentitySpecificationTestBase", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.IdentitySpecificationTestBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Identity.Test.UserManagerSpecificationTestBase", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "SetupIdentityServices", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetupBuilder", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateRoleManager", + "Parameters": [ + { + "Name": "context", + "Type": "System.Object", + "DefaultValue": "null" + }, + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "DefaultValue": "null" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.RoleManager", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddRoleStore", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateTestRole", + "Parameters": [ + { + "Name": "roleNamePrefix", + "Type": "System.String", + "DefaultValue": "\"\"" + }, + { + "Name": "useRoleNamePrefixAsRoleName", + "Type": "System.Boolean", + "DefaultValue": "False" + } + ], + "ReturnType": "T1", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RoleNameEqualsPredicate", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Linq.Expressions.Expression>", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RoleNameStartsWithPredicate", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "ReturnType": "System.Linq.Expressions.Expression>", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanCreateRoleTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BadValidatorBlocksCreateRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChainRoleValidators", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BadValidatorBlocksRoleUpdate", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanDeleteRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanAddRemoveRoleClaim", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanRoleFindById", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanRoleFindByName", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanUpdateRoleName", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanQueryableRoles", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateRoleFailsIfExists", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanAddUsersToRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGetRolesForUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveUserFromRoleWithMultipleRoles", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanRemoveUsersFromRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveUserNotInRoleFails", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserToRoleFailsIfAlreadyInRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserToRolesIgnoresDuplicates", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanFindRoleByNameWithManager", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanFindRoleWithManager", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGetUsersInRole", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.ITestLogger", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_LogMessages", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.TestLogger", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Logging.ILogger", + "Microsoft.AspNetCore.Identity.Test.ITestLogger" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Log", + "Parameters": [ + { + "Name": "logLevel", + "Type": "Microsoft.Extensions.Logging.LogLevel" + }, + { + "Name": "eventId", + "Type": "Microsoft.Extensions.Logging.EventId" + }, + { + "Name": "state", + "Type": "T0" + }, + { + "Name": "exception", + "Type": "System.Exception" + }, + { + "Name": "formatter", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Logging.ILogger", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TState", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "IsEnabled", + "Parameters": [ + { + "Name": "logLevel", + "Type": "Microsoft.Extensions.Logging.LogLevel" + } + ], + "ReturnType": "System.Boolean", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Logging.ILogger", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "BeginScope", + "Parameters": [ + { + "Name": "state", + "Type": "T0" + } + ], + "ReturnType": "System.IDisposable", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Logging.ILogger", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TState", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "get_LogMessages", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.Test.ITestLogger", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TName", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.UserManagerSpecificationTestBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Identity.Test.UserManagerSpecificationTestBase", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.Test.UserManagerSpecificationTestBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "SetupIdentityServices", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetupBuilder", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ShouldSkipDbTests", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateManager", + "Parameters": [ + { + "Name": "context", + "Type": "System.Object", + "DefaultValue": "null" + }, + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "DefaultValue": "null" + }, + { + "Name": "configureServices", + "Type": "System.Action", + "DefaultValue": "null" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.UserManager", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateTestContext", + "Parameters": [], + "ReturnType": "System.Object", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserStore", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "context", + "Type": "System.Object", + "DefaultValue": "null" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserPasswordHash", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "hashedPassword", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateTestUser", + "Parameters": [ + { + "Name": "namePrefix", + "Type": "System.String", + "DefaultValue": "\"\"" + }, + { + "Name": "email", + "Type": "System.String", + "DefaultValue": "\"\"" + }, + { + "Name": "phoneNumber", + "Type": "System.String", + "DefaultValue": "\"\"" + }, + { + "Name": "lockoutEnabled", + "Type": "System.Boolean", + "DefaultValue": "False" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable", + "DefaultValue": "default(System.Nullable)" + }, + { + "Name": "useNamePrefixAsUserName", + "Type": "System.Boolean", + "DefaultValue": "False" + } + ], + "ReturnType": "T0", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserNameEqualsPredicate", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Linq.Expressions.Expression>", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserNameStartsWithPredicate", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "ReturnType": "System.Linq.Expressions.Expression>", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserWillSetCreateDateOnlyIfSupported", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanDeleteUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanUpdateUserName", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CheckSetUserNameValidatesUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameUpdatesSecurityStamp", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUpdatesSecurityStamp", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAuthenticatorKeyUpdatesSecurityStamp", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CheckSetEmailValidatesUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanUpdatePasswordUsingHasher", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanFindById", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserValidatorCanBlockCreate", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserValidatorCanBlockUpdate", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChainUserValidators", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordValidatorCanBlockAddPassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChainPasswordValidators", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordValidatorCanBlockChangePassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordValidatorCanBlockCreateUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanCreateUserNoPassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanCreateUserAddLogin", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanCreateUserLoginAndAddPassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddPasswordFailsIfAlreadyHave", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanCreateUserAddRemoveLogin", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanRemovePassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChangePassword", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanAddRemoveUserClaim", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimOnlyAffectsUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanReplaceUserClaim", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceUserClaimOnlyAffectsUser", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePasswordFallsIfPasswordWrong", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddDupeUserNameFails", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddDupeEmailAllowedByDefault", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddDupeEmailFailsWhenUniqueEmailRequired", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateSecurityStampActuallyChanges", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddDupeLoginFails", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanFindByEmail", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanFindUsersViaUserQuerable", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfirmEmailFalseByDefaultTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanResetPasswordWithStaticTokenProvider", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetPasswordWithStaticTokenProviderFailsWithWrongToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGenerateAndVerifyUserTokenWithStaticTokenProvider", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanConfirmEmailWithStaticToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfirmEmailWithStaticTokenFailsWithWrongToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConfirmTokenFailsAfterPasswordChange", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SingleFailureLockout", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "TwoFailureLockout", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessCountPreventsLockout", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanEnableLockoutManuallyAndLockout", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserNotLockedOutWithNullDateTimeAndIsSetToNullDate", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LockoutFailsIfNotEnabled", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "LockoutEndToUtcNowPlus5ShouldBeLockedOut", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UserLockedOutWithDateTimeLocalKindNowPlus30", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChangePhoneNumber", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberTokenIsInt", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberFailsWithWrongToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberWithCustomProvider", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangePhoneNumberFailsWithWrongPhoneNumber", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanVerifyPhoneNumber", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChangeEmail", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanChangeEmailWithDifferentTokenProvider", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangeEmailTokensFailsAfterEmailChanged", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangeEmailFailsWithWrongToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChangeEmailFailsWithEmail", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EmailFactorFailsAfterSecurityStampChangeTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "EnableTwoFactorChangesSecurityStamp", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateTwoFactorWithUnknownFactorProviderWillThrow", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetValidTwoFactorTestEmptyWithNoProviders", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGetSetUpdateAndRemoveUserToken", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanRedeemRecoveryCodeOnlyOnce", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RecoveryCodesInvalidAfterReplace", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGetValidTwoFactor", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "PhoneFactorFailsAfterSecurityStampChangeTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyTokenFromWrongTokenProviderFails", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "VerifyWithWrongSmsTokenFails", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NullableDateTimeOperationTest", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CanGetUsersWithClaims", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateUsers", + "Parameters": [ + { + "Name": "userNamePrefix", + "Type": "System.String" + }, + { + "Name": "count", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Collections.Generic.List", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "_errorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "ReadOnly": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "NullValue", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Protected", + "GenericParameter": [], + "Constant": true, + "Literal": "\"(null)\"" + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/Stores/IdentityRole.cs b/src/Identity/src/Stores/IdentityRole.cs new file mode 100644 index 0000000000..e3cbef5a24 --- /dev/null +++ b/src/Identity/src/Stores/IdentityRole.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// The default implementation of which uses a string as the primary key. + /// + public class IdentityRole : IdentityRole + { + /// + /// Initializes a new instance of . + /// + /// + /// The Id property is initialized to form a new GUID string value. + /// + public IdentityRole() + { + Id = Guid.NewGuid().ToString(); + } + + /// + /// Initializes a new instance of . + /// + /// The role name. + /// + /// The Id property is initialized to form a new GUID string value. + /// + public IdentityRole(string roleName) : this() + { + Name = roleName; + } + } + + /// + /// Represents a role in the identity system + /// + /// The type used for the primary key for the role. + public class IdentityRole where TKey : IEquatable + { + /// + /// Initializes a new instance of . + /// + public IdentityRole() { } + + /// + /// Initializes a new instance of . + /// + /// The role name. + public IdentityRole(string roleName) : this() + { + Name = roleName; + } + + /// + /// Gets or sets the primary key for this role. + /// + public virtual TKey Id { get; set; } + + /// + /// Gets or sets the name for this role. + /// + public virtual string Name { get; set; } + + /// + /// Gets or sets the normalized name for this role. + /// + public virtual string NormalizedName { get; set; } + + /// + /// A random value that should change whenever a role is persisted to the store + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Returns the name of the role. + /// + /// The name of the role. + public override string ToString() + { + return Name; + } + } +} diff --git a/src/Identity/src/Stores/IdentityRoleClaim.cs b/src/Identity/src/Stores/IdentityRoleClaim.cs new file mode 100644 index 0000000000..ff257d59ed --- /dev/null +++ b/src/Identity/src/Stores/IdentityRoleClaim.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; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a claim that is granted to all users within a role. + /// + /// The type of the primary key of the role associated with this claim. + public class IdentityRoleClaim where TKey : IEquatable + { + /// + /// Gets or sets the identifier for this role claim. + /// + public virtual int Id { get; set; } + + /// + /// Gets or sets the of the primary key of the role associated with this claim. + /// + public virtual TKey RoleId { get; set; } + + /// + /// Gets or sets the claim type for this claim. + /// + public virtual string ClaimType { get; set; } + + /// + /// Gets or sets the claim value for this claim. + /// + public virtual string ClaimValue { get; set; } + + /// + /// Constructs a new claim with the type and value. + /// + /// + public virtual Claim ToClaim() + { + return new Claim(ClaimType, ClaimValue); + } + + /// + /// Initializes by copying ClaimType and ClaimValue from the other claim. + /// + /// The claim to initialize from. + public virtual void InitializeFromClaim(Claim other) + { + ClaimType = other?.Type; + ClaimValue = other?.Value; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Stores/IdentityUser.cs b/src/Identity/src/Stores/IdentityUser.cs new file mode 100644 index 0000000000..e95bb62366 --- /dev/null +++ b/src/Identity/src/Stores/IdentityUser.cs @@ -0,0 +1,152 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// The default implementation of which uses a string as a primary key. + /// + public class IdentityUser : IdentityUser + { + /// + /// Initializes a new instance of . + /// + /// + /// The Id property is initialized to form a new GUID string value. + /// + public IdentityUser() + { + Id = Guid.NewGuid().ToString(); + } + + /// + /// Initializes a new instance of . + /// + /// The user name. + /// + /// The Id property is initialized to form a new GUID string value. + /// + public IdentityUser(string userName) : this() + { + UserName = userName; + } + } + + /// + /// Represents a user in the identity system + /// + /// The type used for the primary key for the user. + public class IdentityUser where TKey : IEquatable + { + /// + /// Initializes a new instance of . + /// + public IdentityUser() { } + + /// + /// Initializes a new instance of . + /// + /// The user name. + public IdentityUser(string userName) : this() + { + UserName = userName; + } + + /// + /// Gets or sets the primary key for this user. + /// + [PersonalData] + public virtual TKey Id { get; set; } + + /// + /// Gets or sets the user name for this user. + /// + [ProtectedPersonalData] + public virtual string UserName { get; set; } + + /// + /// Gets or sets the normalized user name for this user. + /// + public virtual string NormalizedUserName { get; set; } + + /// + /// Gets or sets the email address for this user. + /// + [ProtectedPersonalData] + public virtual string Email { get; set; } + + /// + /// Gets or sets the normalized email address for this user. + /// + public virtual string NormalizedEmail { get; set; } + + /// + /// Gets or sets a flag indicating if a user has confirmed their email address. + /// + /// True if the email address has been confirmed, otherwise false. + [PersonalData] + public virtual bool EmailConfirmed { get; set; } + + /// + /// Gets or sets a salted and hashed representation of the password for this user. + /// + public virtual string PasswordHash { get; set; } + + /// + /// A random value that must change whenever a users credentials change (password changed, login removed) + /// + public virtual string SecurityStamp { get; set; } + + /// + /// A random value that must change whenever a user is persisted to the store + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Gets or sets a telephone number for the user. + /// + [ProtectedPersonalData] + public virtual string PhoneNumber { get; set; } + + /// + /// Gets or sets a flag indicating if a user has confirmed their telephone address. + /// + /// True if the telephone number has been confirmed, otherwise false. + [PersonalData] + public virtual bool PhoneNumberConfirmed { get; set; } + + /// + /// Gets or sets a flag indicating if two factor authentication is enabled for this user. + /// + /// True if 2fa is enabled, otherwise false. + [PersonalData] + public virtual bool TwoFactorEnabled { get; set; } + + /// + /// Gets or sets the date and time, in UTC, when any user lockout ends. + /// + /// + /// A value in the past means the user is not locked out. + /// + public virtual DateTimeOffset? LockoutEnd { get; set; } + + /// + /// Gets or sets a flag indicating if the user could be locked out. + /// + /// True if the user could be locked out, otherwise false. + public virtual bool LockoutEnabled { get; set; } + + /// + /// Gets or sets the number of failed login attempts for the current user. + /// + public virtual int AccessFailedCount { get; set; } + + /// + /// Returns the username for this user. + /// + public override string ToString() + => UserName; + } +} diff --git a/src/Identity/src/Stores/IdentityUserClaim.cs b/src/Identity/src/Stores/IdentityUserClaim.cs new file mode 100644 index 0000000000..bbdd2ac3cf --- /dev/null +++ b/src/Identity/src/Stores/IdentityUserClaim.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; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a claim that a user possesses. + /// + /// The type used for the primary key for this user that possesses this claim. + public class IdentityUserClaim where TKey : IEquatable + { + /// + /// Gets or sets the identifier for this user claim. + /// + public virtual int Id { get; set; } + + /// + /// Gets or sets the primary key of the user associated with this claim. + /// + public virtual TKey UserId { get; set; } + + /// + /// Gets or sets the claim type for this claim. + /// + public virtual string ClaimType { get; set; } + + /// + /// Gets or sets the claim value for this claim. + /// + public virtual string ClaimValue { get; set; } + + /// + /// Converts the entity into a Claim instance. + /// + /// + public virtual Claim ToClaim() + { + return new Claim(ClaimType, ClaimValue); + } + + /// + /// Reads the type and value from the Claim. + /// + /// + public virtual void InitializeFromClaim(Claim claim) + { + ClaimType = claim.Type; + ClaimValue = claim.Value; + } + } +} \ No newline at end of file diff --git a/src/Identity/src/Stores/IdentityUserLogin.cs b/src/Identity/src/Stores/IdentityUserLogin.cs new file mode 100644 index 0000000000..f2c6d165d0 --- /dev/null +++ b/src/Identity/src/Stores/IdentityUserLogin.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a login and its associated provider for a user. + /// + /// The type of the primary key of the user associated with this login. + public class IdentityUserLogin where TKey : IEquatable + { + /// + /// Gets or sets the login provider for the login (e.g. facebook, google) + /// + public virtual string LoginProvider { get; set; } + + /// + /// Gets or sets the unique provider identifier for this login. + /// + public virtual string ProviderKey { get; set; } + + /// + /// Gets or sets the friendly name used in a UI for this login. + /// + public virtual string ProviderDisplayName { get; set; } + + /// + /// Gets or sets the of the primary key of the user associated with this login. + /// + public virtual TKey UserId { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Stores/IdentityUserRole.cs b/src/Identity/src/Stores/IdentityUserRole.cs new file mode 100644 index 0000000000..44613952e7 --- /dev/null +++ b/src/Identity/src/Stores/IdentityUserRole.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents the link between a user and a role. + /// + /// The type of the primary key used for users and roles. + public class IdentityUserRole where TKey : IEquatable + { + /// + /// Gets or sets the primary key of the user that is linked to a role. + /// + public virtual TKey UserId { get; set; } + + /// + /// Gets or sets the primary key of the role that is linked to the user. + /// + public virtual TKey RoleId { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Stores/IdentityUserToken.cs b/src/Identity/src/Stores/IdentityUserToken.cs new file mode 100644 index 0000000000..b03a6976b8 --- /dev/null +++ b/src/Identity/src/Stores/IdentityUserToken.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents an authentication token for a user. + /// + /// The type of the primary key used for users. + public class IdentityUserToken where TKey : IEquatable + { + /// + /// Gets or sets the primary key of the user that the token belongs to. + /// + public virtual TKey UserId { get; set; } + + /// + /// Gets or sets the LoginProvider this token is from. + /// + public virtual string LoginProvider { get; set; } + + /// + /// Gets or sets the name of the token. + /// + public virtual string Name { get; set; } + + /// + /// Gets or sets the token value. + /// + [ProtectedPersonalData] + public virtual string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Identity/src/Stores/Microsoft.Extensions.Identity.Stores.csproj b/src/Identity/src/Stores/Microsoft.Extensions.Identity.Stores.csproj new file mode 100644 index 0000000000..10e6e98997 --- /dev/null +++ b/src/Identity/src/Stores/Microsoft.Extensions.Identity.Stores.csproj @@ -0,0 +1,19 @@ + + + + ASP.NET Core Identity is the membership system for building ASP.NET Core web applications, including membership, login, and user data. ASP.NET Core Identity allows you to add login features to your application and makes it easy to customize data about the logged in user. + netstandard2.0 + true + aspnetcore;identity;membership + + + + + + + + + + + + diff --git a/src/Identity/src/Stores/RoleStoreBase.cs b/src/Identity/src/Stores/RoleStoreBase.cs new file mode 100644 index 0000000000..01ca59bb6c --- /dev/null +++ b/src/Identity/src/Stores/RoleStoreBase.cs @@ -0,0 +1,267 @@ +// 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.ComponentModel; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Internal; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Creates a new instance of a persistence store for roles. + /// + /// The type of the class representing a role. + /// The type of the primary key for a role. + /// The type of the class representing a user role. + /// The type of the class representing a role claim. + public abstract class RoleStoreBase : + IQueryableRoleStore, + IRoleClaimStore + where TRole : IdentityRole + where TKey : IEquatable + where TUserRole : IdentityUserRole, new() + where TRoleClaim : IdentityRoleClaim, new() + { + /// + /// Constructs a new instance of . + /// + /// The . + public RoleStoreBase(IdentityErrorDescriber describer) + { + if (describer == null) + { + throw new ArgumentNullException(nameof(describer)); + } + + ErrorDescriber = describer; + } + + private bool _disposed; + + /// + /// Gets or sets the for any error that occurred with the current operation. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Creates a new role in a store as an asynchronous operation. + /// + /// The role to create in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public abstract Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Updates a role in a store as an asynchronous operation. + /// + /// The role to update in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public abstract Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Deletes a role from the store as an asynchronous operation. + /// + /// The role to delete from the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public abstract Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Gets the ID for a role from the store as an asynchronous operation. + /// + /// The role whose ID should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the ID of the role. + public virtual Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(ConvertIdToString(role.Id)); + } + + /// + /// Gets the name of a role from the store as an asynchronous operation. + /// + /// The role whose name should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public virtual Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(role.Name); + } + + /// + /// Sets the name of a role in the store as an asynchronous operation. + /// + /// The role whose name should be set. + /// The name of the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + role.Name = roleName; + return Task.CompletedTask; + } + + /// + /// Converts the provided to a strongly typed key object. + /// + /// The id to convert. + /// An instance of representing the provided . + public virtual TKey ConvertIdFromString(string id) + { + if (id == null) + { + return default(TKey); + } + return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id); + } + + /// + /// Converts the provided to its string representation. + /// + /// The id to convert. + /// An representation of the provided . + public virtual string ConvertIdToString(TKey id) + { + if (id.Equals(default(TKey))) + { + return null; + } + return id.ToString(); + } + + /// + /// Finds the role who has the specified ID as an asynchronous operation. + /// + /// The role ID to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public abstract Task FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Finds the role who has the specified normalized name as an asynchronous operation. + /// + /// The normalized role name to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public abstract Task FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Get a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public virtual Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + return Task.FromResult(role.NormalizedName); + } + + /// + /// Set a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be set. + /// The normalized name to set + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException(nameof(role)); + } + role.NormalizedName = normalizedName; + return Task.CompletedTask; + } + + /// + /// Throws if this class has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + /// + /// Dispose the stores + /// + public void Dispose() => _disposed = true; + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The role whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a role. + public abstract Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Adds the given to the specified . + /// + /// The role to add the claim to. + /// The claim to add to the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Removes the given from the specified . + /// + /// The role to remove the claim from. + /// The claim to remove from the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// A navigation property for the roles the store contains. + /// + public abstract IQueryable Roles + { + get; + } + + /// + /// Creates a entity representing a role claim. + /// + /// The associated role. + /// The associated claim. + /// The role claim entity. + protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim) + => new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }; + } +} diff --git a/src/Identity/src/Stores/UserStoreBase.cs b/src/Identity/src/Stores/UserStoreBase.cs new file mode 100644 index 0000000000..a8259092ca --- /dev/null +++ b/src/Identity/src/Stores/UserStoreBase.cs @@ -0,0 +1,1184 @@ +// 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.ComponentModel; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Represents a new instance of a persistence store for the specified user type. + /// + /// The type representing a user. + /// The type of the primary key for a user. + /// The type representing a claim. + /// The type representing a user external login. + /// The type representing a user token. + public abstract class UserStoreBase : + IUserLoginStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore, + IQueryableUserStore, + IUserTwoFactorStore, + IUserAuthenticationTokenStore, + IUserAuthenticatorKeyStore, + IUserTwoFactorRecoveryCodeStore + where TUser : IdentityUser + where TKey : IEquatable + where TUserClaim : IdentityUserClaim, new() + where TUserLogin : IdentityUserLogin, new() + where TUserToken : IdentityUserToken, new() + { + /// + /// Creates a new instance. + /// + /// The used to describe store errors. + public UserStoreBase(IdentityErrorDescriber describer) + { + if (describer == null) + { + throw new ArgumentNullException(nameof(describer)); + } + + ErrorDescriber = describer; + } + + private bool _disposed; + + /// + /// Gets or sets the for any error that occurred with the current operation. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Called to create a new instance of a . + /// + /// The associated user. + /// The associated claim. + /// + protected virtual TUserClaim CreateUserClaim(TUser user, Claim claim) + { + var userClaim = new TUserClaim { UserId = user.Id }; + userClaim.InitializeFromClaim(claim); + return userClaim; + } + + /// + /// Called to create a new instance of a . + /// + /// The associated user. + /// The sasociated login. + /// + protected virtual TUserLogin CreateUserLogin(TUser user, UserLoginInfo login) + { + return new TUserLogin + { + UserId = user.Id, + ProviderKey = login.ProviderKey, + LoginProvider = login.LoginProvider, + ProviderDisplayName = login.ProviderDisplayName + }; + } + + /// + /// Called to create a new instance of a . + /// + /// The associated user. + /// The associated login provider. + /// The name of the user token. + /// The value of the user token. + /// + protected virtual TUserToken CreateUserToken(TUser user, string loginProvider, string name, string value) + { + return new TUserToken + { + UserId = user.Id, + LoginProvider = loginProvider, + Name = name, + Value = value + }; + } + + /// + /// Gets the user identifier for the specified . + /// + /// The user whose identifier should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the identifier for the specified . + public virtual Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(ConvertIdToString(user.Id)); + } + + /// + /// Gets the user name for the specified . + /// + /// The user whose name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the name for the specified . + public virtual Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.UserName); + } + + /// + /// Sets the given for the specified . + /// + /// The user whose name should be set. + /// The user name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.UserName = userName; + return Task.CompletedTask; + } + + /// + /// Gets the normalized user name for the specified . + /// + /// The user whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the normalized user name for the specified . + public virtual Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.NormalizedUserName); + } + + /// + /// Sets the given normalized name for the specified . + /// + /// The user whose name should be set. + /// The normalized name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.NormalizedUserName = normalizedName; + return Task.CompletedTask; + } + + /// + /// Creates the specified in the user store. + /// + /// The user to create. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the creation operation. + public abstract Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Updates the specified in the user store. + /// + /// The user to update. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public abstract Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Deletes the specified from the user store. + /// + /// The user to delete. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public abstract Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public abstract Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Converts the provided to a strongly typed key object. + /// + /// The id to convert. + /// An instance of representing the provided . + public virtual TKey ConvertIdFromString(string id) + { + if (id == null) + { + return default(TKey); + } + return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id); + } + + /// + /// Converts the provided to its string representation. + /// + /// The id to convert. + /// An representation of the provided . + public virtual string ConvertIdToString(TKey id) + { + if (object.Equals(id, default(TKey))) + { + return null; + } + return id.ToString(); + } + + /// + /// Finds and returns a user, if any, who has the specified normalized user name. + /// + /// The normalized user name to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public abstract Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// A navigation property for the users the store contains. + /// + public abstract IQueryable Users + { + get; + } + + /// + /// Sets the password hash for a user. + /// + /// The user to set the password hash for. + /// The password hash to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.PasswordHash = passwordHash; + return Task.CompletedTask; + } + + /// + /// Gets the password hash for a user. + /// + /// The user to retrieve the password hash for. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the password hash for the user. + public virtual Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.PasswordHash); + } + + /// + /// Returns a flag indicating if the specified user has a password. + /// + /// The user to retrieve the password hash for. + /// The used to propagate notifications that the operation should be canceled. + /// A containing a flag indicating if the specified user has a password. If the + /// user has a password the returned value with be true, otherwise it will be false. + public virtual Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.FromResult(user.PasswordHash != null); + } + + /// + /// Return a user with the matching userId if it exists. + /// + /// The user's id. + /// The used to propagate notifications that the operation should be canceled. + /// The user if it exists. + protected abstract Task FindUserAsync(TKey userId, CancellationToken cancellationToken); + + /// + /// Return a user login with the matching userId, provider, providerKey if it exists. + /// + /// The user's id. + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected abstract Task FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken); + + /// + /// Return a user login with provider, providerKey if it exists. + /// + /// The login provider name. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The user login if it exists. + protected abstract Task FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken); + + /// + /// Throws if this class has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + /// + /// Dispose the store + /// + public void Dispose() + { + _disposed = true; + } + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The user whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a user. + public abstract Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Adds the given to the specified . + /// + /// The user to add the claim to. + /// The claim to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Replaces the on the specified , with the . + /// + /// The user to replace the claim on. + /// The claim replace. + /// The new claim replacing the . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Removes the given from the specified . + /// + /// The user to remove the claims from. + /// The claim to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Adds the given to the specified . + /// + /// The user to add the login to. + /// The login to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Removes the given from the specified . + /// + /// The user to remove the login from. + /// The login to remove from the user. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + public abstract Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Retrieves the user associated with the specified login provider and login provider key.. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + public async virtual Task FindByLoginAsync(string loginProvider, string providerKey, + CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken); + if (userLogin != null) + { + return await FindUserAsync(userLogin.UserId, cancellationToken); + } + return null; + } + + /// + /// Gets a flag indicating whether the email address for the specified has been verified, true if the email address is verified otherwise + /// false. + /// + /// The user whose email confirmation status should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified + /// has been confirmed or not. + /// + public virtual Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.EmailConfirmed); + } + + /// + /// Sets the flag indicating whether the specified 's email address has been confirmed or not. + /// + /// The user whose email confirmation status should be set. + /// A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.EmailConfirmed = confirmed; + return Task.CompletedTask; + } + + /// + /// Sets the address for a . + /// + /// The user whose email should be set. + /// The email to set. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.Email = email; + return Task.CompletedTask; + } + + /// + /// Gets the email address for the specified . + /// + /// The user whose email should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// The task object containing the results of the asynchronous operation, the email address for the specified . + public virtual Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.Email); + } + + /// + /// Returns the normalized email for the specified . + /// + /// The user whose email address to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user. + /// + public virtual Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.NormalizedEmail); + } + + /// + /// Sets the normalized email for the specified . + /// + /// The user whose email address to set. + /// The normalized email to set for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.NormalizedEmail = normalizedEmail; + return Task.CompletedTask; + } + + /// + /// Gets the user, if any, associated with the specified, normalized email address. + /// + /// The normalized email address to return the user for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address. + /// + public abstract Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Gets the last a user's last lockout expired, if any. + /// Any time in the past should be indicates a user is not locked out. + /// + /// The user whose lockout date should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a containing the last time + /// a user's lockout expired, if any. + /// + public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.LockoutEnd); + } + + /// + /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user. + /// + /// The user whose lockout date should be set. + /// The after which the 's lockout should end. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.LockoutEnd = lockoutEnd; + return Task.CompletedTask; + } + + /// + /// Records that a failed access has occurred, incrementing the failed access count. + /// + /// The user whose cancellation count should be incremented. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the incremented failed access count. + public virtual Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Resets a user's failed access count. + /// + /// The user whose failed access count should be reset. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + /// This is typically called after the account is successfully accessed. + public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.AccessFailedCount = 0; + return Task.CompletedTask; + } + + /// + /// Retrieves the current failed access count for the specified .. + /// + /// The user whose failed access count should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the failed access count. + public virtual Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Retrieves a flag indicating whether user lockout can enabled for the specified user. + /// + /// The user whose ability to be locked out should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, true if a user can be locked out, otherwise false. + /// + public virtual Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.LockoutEnabled); + } + + /// + /// Set the flag indicating if the specified can be locked out.. + /// + /// The user whose ability to be locked out should be set. + /// A flag indicating if lock out can be enabled for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.LockoutEnabled = enabled; + return Task.CompletedTask; + } + + /// + /// Sets the telephone number for the specified . + /// + /// The user whose telephone number should be set. + /// The telephone number to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.PhoneNumber = phoneNumber; + return Task.CompletedTask; + } + + /// + /// Gets the telephone number, if any, for the specified . + /// + /// The user whose telephone number should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the user's telephone number, if any. + public virtual Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.PhoneNumber); + } + + /// + /// Gets a flag indicating whether the specified 's telephone number has been confirmed. + /// + /// The user to return a flag for, indicating whether their telephone number is confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, returning true if the specified has a confirmed + /// telephone number otherwise false. + /// + public virtual Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.PhoneNumberConfirmed); + } + + /// + /// Sets a flag indicating if the specified 's phone number has been confirmed.. + /// + /// The user whose telephone number confirmation status should be set. + /// A flag indicating whether the user's telephone number has been confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.PhoneNumberConfirmed = confirmed; + return Task.CompletedTask; + } + + /// + /// Sets the provided security for the specified . + /// + /// The user whose security stamp should be set. + /// The security stamp to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (stamp == null) + { + throw new ArgumentNullException(nameof(stamp)); + } + user.SecurityStamp = stamp; + return Task.CompletedTask; + } + + /// + /// Get the security stamp for the specified . + /// + /// The user whose security stamp should be set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + public virtual Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.SecurityStamp); + } + + /// + /// Sets a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// A flag indicating whether the specified has two factor authentication enabled. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + user.TwoFactorEnabled = enabled; + return Task.CompletedTask; + } + + /// + /// Returns a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing a flag indicating whether the specified + /// has two factor authentication enabled or not. + /// + public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + return Task.FromResult(user.TwoFactorEnabled); + } + + /// + /// Retrieves all users with the specified claim. + /// + /// The claim whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that contain the specified claim. + /// + public abstract Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Find a user token if it exists. + /// + /// The token owner. + /// The login provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The user token if it exists. + protected abstract Task FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken); + + /// + /// Add a new user token. + /// + /// The token to be added. + /// + protected abstract Task AddUserTokenAsync(TUserToken token); + + /// + /// Remove a new user token. + /// + /// The token to be removed. + /// + protected abstract Task RemoveUserTokenAsync(TUserToken token); + + /// + /// Sets the token value for a particular user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The value of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var token = await FindTokenAsync(user, loginProvider, name, cancellationToken); + if (token == null) + { + await AddUserTokenAsync(CreateUserToken(user, loginProvider, name, value)); + } + else + { + token.Value = value; + } + } + + /// + /// Deletes a token for a user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken); + if (entry != null) + { + await RemoveUserTokenAsync(entry); + } + } + + /// + /// Returns the token value. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken); + return entry?.Value; + } + + private const string InternalLoginProvider = "[AspNetUserStore]"; + private const string AuthenticatorKeyTokenName = "AuthenticatorKey"; + private const string RecoveryCodeTokenName = "RecoveryCodes"; + + /// + /// Sets the authenticator key for the specified . + /// + /// The user whose authenticator key should be set. + /// The authenticator key to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken) + => SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken); + + /// + /// Get the authenticator key for the specified . + /// + /// The user whose security stamp should be set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + public virtual Task GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken) + => GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken); + + /// + /// Returns how many recovery code are still valid for a user. + /// + /// The user who owns the recovery code. + /// The used to propagate notifications that the operation should be canceled. + /// The number of valid recovery codes for the user.. + public virtual async Task CountCodesAsync(TUser user, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? ""; + if (mergedCodes.Length > 0) + { + return mergedCodes.Split(';').Length; + } + return 0; + } + + /// + /// Updates the recovery codes for the user while invalidating any previous recovery codes. + /// + /// The user to store new recovery codes for. + /// The new recovery codes for the user. + /// The used to propagate notifications that the operation should be canceled. + /// The new recovery codes for the user. + public virtual Task ReplaceCodesAsync(TUser user, IEnumerable recoveryCodes, CancellationToken cancellationToken) + { + var mergedCodes = string.Join(";", recoveryCodes); + return SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken); + } + + /// + /// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid + /// once, and will be invalid after use. + /// + /// The user who owns the recovery code. + /// The recovery code to use. + /// The used to propagate notifications that the operation should be canceled. + /// True if the recovery code was found for the user. + public virtual async Task RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + if (code == null) + { + throw new ArgumentNullException(nameof(code)); + } + + var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? ""; + var splitCodes = mergedCodes.Split(';'); + if (splitCodes.Contains(code)) + { + var updatedCodes = new List(splitCodes.Where(s => s != code)); + await ReplaceCodesAsync(user, updatedCodes, cancellationToken); + return true; + } + return false; + } + } + + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + /// The type representing a user. + /// The type representing a role. + /// The type of the primary key for a role. + /// The type representing a claim. + /// The type representing a user role. + /// The type representing a user external login. + /// The type representing a user token. + /// The type representing a role claim. + public abstract class UserStoreBase : + UserStoreBase, + IUserRoleStore + where TUser : IdentityUser + where TRole : IdentityRole + where TKey : IEquatable + where TUserClaim : IdentityUserClaim, new() + where TUserRole : IdentityUserRole, new() + where TUserLogin : IdentityUserLogin, new() + where TUserToken : IdentityUserToken, new() + where TRoleClaim : IdentityRoleClaim, new() + { + /// + /// Creates a new instance. + /// + /// The used to describe store errors. + public UserStoreBase(IdentityErrorDescriber describer) : base(describer) { } + + /// + /// Called to create a new instance of a . + /// + /// The associated user. + /// The associated role. + /// + protected virtual TUserRole CreateUserRole(TUser user, TRole role) + { + return new TUserRole() + { + UserId = user.Id, + RoleId = role.Id + }; + } + + + /// + /// Retrieves all users in the specified role. + /// + /// The role whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that are in the specified role. + /// + public abstract Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Adds the given to the specified . + /// + /// The user to add the role to. + /// The role to add. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Removes the given from the specified . + /// + /// The user to remove the role from. + /// The role to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public abstract Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Retrieves the roles the specified is a member of. + /// + /// The user whose roles should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the roles the user is a member of. + public abstract Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Returns a flag indicating if the specified user is a member of the give . + /// + /// The user whose role membership should be checked. + /// The role to check membership of + /// The used to propagate notifications that the operation should be canceled. + /// A containing a flag indicating if the specified user is a member of the given group. If the + /// user is a member of the group the returned value with be true, otherwise it will be false. + public abstract Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Return a role with the normalized name if it exists. + /// + /// The normalized role name. + /// The used to propagate notifications that the operation should be canceled. + /// The role if it exists. + protected abstract Task FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken); + + /// + /// Return a user role for the userId and roleId if it exists. + /// + /// The user's id. + /// The role's id. + /// The used to propagate notifications that the operation should be canceled. + /// The user role if it exists. + protected abstract Task FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken); + } +} diff --git a/src/Identity/src/Stores/baseline.netcore.json b/src/Identity/src/Stores/baseline.netcore.json new file mode 100644 index 0000000000..a046318800 --- /dev/null +++ b/src/Identity/src/Stores/baseline.netcore.json @@ -0,0 +1,3380 @@ +{ + "AssemblyIdentity": "Microsoft.Extensions.Identity.Stores, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.Identity.IdentityRole", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.IdentityRole", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityRole", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Name", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NormalizedName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConcurrencyStamp", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConcurrencyStamp", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "roleName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityRoleClaim", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimValue", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimValue", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToClaim", + "Parameters": [], + "ReturnType": "System.Security.Claims.Claim", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InitializeFromClaim", + "Parameters": [ + { + "Name": "other", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUser", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Identity.IdentityUser", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUser", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NormalizedUserName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedUserName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Email", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Email", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NormalizedEmail", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NormalizedEmail", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_EmailConfirmed", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_EmailConfirmed", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PasswordHash", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PasswordHash", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityStamp", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityStamp", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConcurrencyStamp", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConcurrencyStamp", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PhoneNumber", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PhoneNumber", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_PhoneNumberConfirmed", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_PhoneNumberConfirmed", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TwoFactorEnabled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TwoFactorEnabled", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LockoutEnd", + "Parameters": [], + "ReturnType": "System.Nullable", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LockoutEnd", + "Parameters": [ + { + "Name": "value", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LockoutEnabled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LockoutEnabled", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AccessFailedCount", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AccessFailedCount", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "userName", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUserClaim", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Id", + "Parameters": [], + "ReturnType": "System.Int32", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Id", + "Parameters": [ + { + "Name": "value", + "Type": "System.Int32" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimType", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClaimValue", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ClaimValue", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToClaim", + "Parameters": [], + "ReturnType": "System.Security.Claims.Claim", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "InitializeFromClaim", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUserLogin", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_LoginProvider", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LoginProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderKey", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderKey", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ProviderDisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProviderDisplayName", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UserId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUserRole", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RoleId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RoleId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityUserToken", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_UserId", + "Parameters": [], + "ReturnType": "T0", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UserId", + "Parameters": [ + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LoginProvider", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_LoginProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Name", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Name", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Value", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Value", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TKey", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.RoleStoreBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Microsoft.AspNetCore.Identity.IRoleClaimStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleIdAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "roleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedRoleNameAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "id", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ErrorDescriber", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdFromString", + "Parameters": [ + { + "Name": "id", + "Type": "System.String" + } + ], + "ReturnType": "T1", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdToString", + "Parameters": [ + { + "Name": "id", + "Type": "T1" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimAsync", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IRoleClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Roles", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IQueryableRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateRoleClaim", + "Parameters": [ + { + "Name": "role", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "T3", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TRole", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 2, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserRole" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRoleClaim" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserStoreBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Microsoft.AspNetCore.Identity.IUserSecurityStampStore", + "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Microsoft.AspNetCore.Identity.IUserTwoFactorStore", + "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore", + "Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetUserIdAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "userName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedUserNameAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UpdateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByIdAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByNameAsync", + "Parameters": [ + { + "Name": "normalizedUserName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Dispose", + "Parameters": [], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "System.IDisposable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorDescriber", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ErrorDescriber", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserClaim", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + } + ], + "ReturnType": "T2", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserLogin", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + } + ], + "ReturnType": "T3", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateUserToken", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "T4", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdFromString", + "Parameters": [ + { + "Name": "id", + "Type": "System.String" + } + ], + "ReturnType": "T1", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ConvertIdToString", + "Parameters": [ + { + "Name": "id", + "Type": "T1" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Users", + "Parameters": [], + "ReturnType": "System.Linq.IQueryable", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IQueryableUserStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "passwordHash", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPasswordHashAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HasPasswordAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPasswordStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T1" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T1" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ThrowIfDisposed", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceClaimAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "newClaim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveClaimsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "claims", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "login", + "Type": "Microsoft.AspNetCore.Identity.UserLoginInfo" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveLoginAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLoginsAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByLoginAsync", + "Parameters": [ + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "providerKey", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLoginStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "email", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetNormalizedEmailAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindByEmailAsync", + "Parameters": [ + { + "Name": "normalizedEmail", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserEmailStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEndDateAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "lockoutEnd", + "Type": "System.Nullable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IncrementAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ResetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAccessFailedCountAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetLockoutEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserLockoutStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "phoneNumber", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetPhoneNumberConfirmedAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "confirmed", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserPhoneNumberStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "stamp", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserSecurityStampStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetSecurityStampAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserSecurityStampStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "enabled", + "Type": "System.Boolean" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTwoFactorEnabledAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersForClaimAsync", + "Parameters": [ + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserClaimStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T4" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveUserTokenAsync", + "Parameters": [ + { + "Name": "token", + "Type": "T4" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetTokenAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "loginProvider", + "Type": "System.String" + }, + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetAuthenticatorKeyAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CountCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReplaceCodesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "recoveryCodes", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RedeemCodeAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "code", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 2, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UserStoreBase", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Identity.UserStoreBase", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Identity.IUserRoleStore" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateUserRole", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "role", + "Type": "T1" + } + ], + "ReturnType": "T4", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetUsersInRoleAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddToRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveFromRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRolesAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "IsInRoleAsync", + "Parameters": [ + { + "Name": "user", + "Type": "T0" + }, + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken", + "DefaultValue": "default(System.Threading.CancellationToken)" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "ImplementedInterface": "Microsoft.AspNetCore.Identity.IUserRoleStore", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindRoleAsync", + "Parameters": [ + { + "Name": "normalizedRoleName", + "Type": "System.String" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "FindUserRoleAsync", + "Parameters": [ + { + "Name": "userId", + "Type": "T2" + }, + { + "Name": "roleId", + "Type": "T2" + }, + { + "Name": "cancellationToken", + "Type": "System.Threading.CancellationToken" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "describer", + "Type": "Microsoft.AspNetCore.Identity.IdentityErrorDescriber" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUser" + ] + }, + { + "ParameterName": "TRole", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRole" + ] + }, + { + "ParameterName": "TKey", + "ParameterPosition": 2, + "BaseTypeOrInterfaces": [ + "System.IEquatable" + ] + }, + { + "ParameterName": "TUserClaim", + "ParameterPosition": 3, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserClaim" + ] + }, + { + "ParameterName": "TUserRole", + "ParameterPosition": 4, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserRole" + ] + }, + { + "ParameterName": "TUserLogin", + "ParameterPosition": 5, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserLogin" + ] + }, + { + "ParameterName": "TUserToken", + "ParameterPosition": 6, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityUserToken" + ] + }, + { + "ParameterName": "TRoleClaim", + "ParameterPosition": 7, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Identity.IdentityRoleClaim" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Filters/ExternalLoginsPageFilter.cs b/src/Identity/src/UI/Areas/Identity/Filters/ExternalLoginsPageFilter.cs new file mode 100644 index 0000000000..75719adc01 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Filters/ExternalLoginsPageFilter.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Identity.UI.Areas.Identity.Filters +{ + internal class ExternalLoginsPageFilter : IAsyncPageFilter where TUser : class + { + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + var result = await next(); + if (result.Result is PageResult page) + { + var signInManager = context.HttpContext.RequestServices.GetRequiredService>(); + var schemes = await signInManager.GetExternalAuthenticationSchemesAsync(); + var hasExternalLogins = schemes.Any(); + + page.ViewData["ManageNav.HasExternalLogins"] = hasExternalLogins; + } + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml new file mode 100644 index 0000000000..3510edef22 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,10 @@ +@page +@model AccessDeniedModel +@{ + ViewData["Title"] = "Access denied"; +} + +
+

@ViewData["Title"]

+

You do not have access to this resource.

+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 0000000000..0d0399307d --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + public class AccessDeniedModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml new file mode 100644 index 0000000000..903896477c --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml @@ -0,0 +1,12 @@ +@page +@model ConfirmEmailModel +@{ + ViewData["Title"] = "Confirm email"; +} + +

@ViewData["Title"]

+
+

+ Thank you for confirming your email. +

+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs new file mode 100644 index 0000000000..0538c916d1 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs @@ -0,0 +1,50 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(ConfirmEmailModel<>))] + public abstract class ConfirmEmailModel : PageModel + { + public virtual Task OnGetAsync(string userId, string code) => throw new NotImplementedException(); + } + + internal class ConfirmEmailModel : ConfirmEmailModel where TUser : class + { + private readonly UserManager _userManager; + + public ConfirmEmailModel(UserManager userManager) + { + _userManager = userManager; + } + + public override async Task OnGetAsync(string userId, string code) + { + if (userId == null || code == null) + { + return RedirectToPage("/Index"); + } + + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return NotFound($"Unable to load user with ID '{userId}'."); + } + + var result = await _userManager.ConfirmEmailAsync(user, code); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':"); + } + + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml new file mode 100644 index 0000000000..ba074852ee --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml @@ -0,0 +1,33 @@ +@page +@model ExternalLoginModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+

Associate your @Model.LoginProvider account.

+
+ +

+ You've successfully authenticated with @Model.LoginProvider. + Please enter an email address for this site below and click the Register button to finish + logging in. +

+ +
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs new file mode 100644 index 0000000000..f57f6c7ad4 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs @@ -0,0 +1,185 @@ +// 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.ComponentModel.DataAnnotations; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(ExternalLoginModel<>))] + public class ExternalLoginModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public string LoginProvider { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public virtual IActionResult OnGet() => throw new NotImplementedException(); + + public virtual IActionResult OnPost(string provider, string returnUrl = null) => throw new NotImplementedException(); + + public virtual Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) => throw new NotImplementedException(); + + public virtual Task OnPostConfirmationAsync(string returnUrl = null) => throw new NotImplementedException(); + } + + internal class ExternalLoginModel : ExternalLoginModel where TUser : class + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly IUserStore _userStore; + private readonly IUserEmailStore _emailStore; + private readonly ILogger _logger; + + public ExternalLoginModel( + SignInManager signInManager, + UserManager userManager, + IUserStore userStore, + ILogger logger) + { + _signInManager = signInManager; + _userManager = userManager; + _userStore = userStore; + _emailStore = GetEmailStore(); + _logger = logger; + } + + public override IActionResult OnGet() + { + return RedirectToPage("./Login"); + } + + public override IActionResult OnPost(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + public override async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if (remoteError != null) + { + ErrorMessage = $"Error from external provider: {remoteError}"; + return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); + } + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + ErrorMessage = "Error loading external login information."; + return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); + if (result.Succeeded) + { + _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); + return LocalRedirect(returnUrl); + } + if (result.IsLockedOut) + { + return RedirectToPage("./Lockout"); + } + else + { + // If the user does not have an account, then ask the user to create an account. + ReturnUrl = returnUrl; + LoginProvider = info.LoginProvider; + if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) + { + Input = new InputModel + { + Email = info.Principal.FindFirstValue(ClaimTypes.Email) + }; + } + return Page(); + } + } + + public override async Task OnPostConfirmationAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + ErrorMessage = "Error loading external login information during confirmation."; + return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); + } + + if (ModelState.IsValid) + { + var user = CreateUser(); + + await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + + var result = await _userManager.CreateAsync(user); + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); + return LocalRedirect(returnUrl); + } + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + LoginProvider = info.LoginProvider; + ReturnUrl = returnUrl; + return Page(); + } + + private TUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(TUser)}'. " + + $"Ensure that '{nameof(TUser)}' is not an abstract class and has a parameterless constructor, or alternatively " + + $"override the external login page in /Areas/Identity/Pages/Account/ExternalLogin.cshtml"); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!_userManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)_userStore; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml new file mode 100644 index 0000000000..5cdfdc0a89 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml @@ -0,0 +1,26 @@ +@page +@model ForgotPasswordModel +@{ + ViewData["Title"] = "Forgot your password?"; +} + +

@ViewData["Title"]

+

Enter your email.

+
+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs new file mode 100644 index 0000000000..b3ba40c5e6 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs @@ -0,0 +1,74 @@ +// 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.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(ForgotPasswordModel<>))] + public abstract class ForgotPasswordModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class ForgotPasswordModel : ForgotPasswordModel where TUser : class + { + private readonly UserManager _userManager; + private readonly IEmailSender _emailSender; + + public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) + { + _userManager = userManager; + _emailSender = emailSender; + } + + public override async Task OnPostAsync() + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + var code = await _userManager.GeneratePasswordResetTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ResetPassword", + pageHandler: null, + values: new { code }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync( + Input.Email, + "Reset Password", + $"Please reset your password by clicking here."); + + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 0000000000..b1139309eb --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,10 @@ +@page +@model ForgotPasswordConfirmation +@{ + ViewData["Title"] = "Forgot password confirmation"; +} + +

@ViewData["Title"]

+

+ Please check your email to reset your password. +

diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs new file mode 100644 index 0000000000..c88d1d1fd5 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs @@ -0,0 +1,16 @@ +// 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.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + public class ForgotPasswordConfirmation : PageModel + { + public void OnGet() + { + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml new file mode 100644 index 0000000000..4eded88208 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml @@ -0,0 +1,10 @@ +@page +@model LockoutModel +@{ + ViewData["Title"] = "Locked out"; +} + +
+

@ViewData["Title"]

+

This account has been locked out, please try again later.

+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml.cs new file mode 100644 index 0000000000..d48564c5ad --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Lockout.cshtml.cs @@ -0,0 +1,16 @@ +// 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.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + public class LockoutModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml new file mode 100644 index 0000000000..875fe3ee7f --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml @@ -0,0 +1,82 @@ +@page +@model LoginModel + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"]

+
+
+
+
+

Use a local account to log in.

+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+

Use another service to log in.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 0000000000..6c01edd524 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,112 @@ +// 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.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(LoginModel<>))] + public abstract class LoginModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public IList ExternalLogins { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public virtual Task OnGetAsync(string returnUrl = null) => throw new NotImplementedException(); + + public virtual Task OnPostAsync(string returnUrl = null) => throw new NotImplementedException(); + } + + internal class LoginModel : LoginModel where TUser : class + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); + } + + returnUrl = returnUrl ?? Url.Content("~/"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public override async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); + if (result.Succeeded) + { + _logger.LogInformation("User logged in."); + return LocalRedirect(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml new file mode 100644 index 0000000000..94242c929e --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml @@ -0,0 +1,41 @@ +@page +@model LoginWith2faModel +@{ + ViewData["Title"] = "Two-factor authentication"; +} + +

@ViewData["Title"]

+
+

Your login is protected with an authenticator app. Enter your authenticator code below.

+
+
+
+ +
+
+ + + +
+
+
+ +
+
+
+ +
+
+
+
+

+ Don't have access to your authenticator device? You can + log in with a recovery code. +

+ +@section Scripts { + +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs new file mode 100644 index 0000000000..3bda67df0b --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs @@ -0,0 +1,113 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(LoginWith2faModel<>))] + public abstract class LoginWith2faModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public bool RememberMe { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + } + + public virtual Task OnGetAsync(bool rememberMe, string returnUrl = null) => throw new NotImplementedException(); + + public virtual Task OnPostAsync(bool rememberMe, string returnUrl = null) => throw new NotImplementedException(); + } + + internal class LoginWith2faModel : LoginWith2faModel where TUser : class + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public LoginWith2faModel( + SignInManager signInManager, + UserManager userManager, + ILogger logger) + { + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + } + + public override async Task OnGetAsync(bool rememberMe, string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + ReturnUrl = returnUrl; + RememberMe = rememberMe; + + return Page(); + } + + public override async Task OnPostAsync(bool rememberMe, string returnUrl = null) + { + if (!ModelState.IsValid) + { + return Page(); + } + + returnUrl = returnUrl ?? Url.Content("~/"); + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); + + var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine); + + var userId = await _userManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", userId); + return LocalRedirect(returnUrl); + } + else if (result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", userId); + return RedirectToPage("./Lockout"); + } + else + { + _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId); + ModelState.AddModelError(string.Empty, "Invalid authenticator code."); + return Page(); + } + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml new file mode 100644 index 0000000000..abaad2a4d4 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml @@ -0,0 +1,29 @@ +@page +@model LoginWithRecoveryCodeModel +@{ + ViewData["Title"] = "Recovery code verification"; +} + +

@ViewData["Title"]

+
+

+ You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. +

+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs new file mode 100644 index 0000000000..47a65af546 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs @@ -0,0 +1,104 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(LoginWithRecoveryCodeModel<>))] + public abstract class LoginWithRecoveryCodeModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [BindProperty] + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } + } + + public virtual Task OnGetAsync(string returnUrl = null) => throw new NotImplementedException(); + + public virtual Task OnPostAsync(string returnUrl = null) => throw new NotImplementedException(); + } + + internal class LoginWithRecoveryCodeModel : LoginWithRecoveryCodeModel where TUser: class + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public LoginWithRecoveryCodeModel( + SignInManager signInManager, + UserManager userManager, + ILogger logger) + { + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + } + + public override async Task OnGetAsync(string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + ReturnUrl = returnUrl; + + return Page(); + } + + public override async Task OnPostAsync(string returnUrl = null) + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if (user == null) + { + throw new InvalidOperationException($"Unable to load two-factor authentication user."); + } + + var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); + + var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + var userId = await _userManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", userId); + return LocalRedirect(returnUrl ?? Url.Content("~/")); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", userId); + return RedirectToPage("./Lockout"); + } + else + { + _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId); + ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); + return Page(); + } + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml new file mode 100644 index 0000000000..8e57434001 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml @@ -0,0 +1,10 @@ +@page +@model LogoutModel +@{ + ViewData["Title"] = "Log out"; +} + +
+

@ViewData["Title"]

+

You have successfully logged out of the application.

+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs new file mode 100644 index 0000000000..e82b846ca0 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -0,0 +1,51 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(LogoutModel<>))] + public abstract class LogoutModel : PageModel + { + public void OnGet() + { + } + + public virtual Task OnPost(string returnUrl = null) => throw new NotImplementedException(); + } + + internal class LogoutModel : LogoutModel where TUser : class + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LogoutModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnPost(string returnUrl = null) + { + await _signInManager.SignOutAsync(); + _logger.LogInformation("User logged out."); + if (returnUrl != null) + { + return LocalRedirect(returnUrl); + } + else + { + // This needs to be a redirect so that the browser performs a new + // request and the identity for the user gets updated. + return RedirectToPage(); + } + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml new file mode 100644 index 0000000000..21cfadb73d --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml @@ -0,0 +1,36 @@ +@page +@model ChangePasswordModel +@{ + ViewData["Title"] = "Change password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

@ViewData["Title"]

+@Html.Partial("_StatusMessage", Model.StatusMessage) +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs new file mode 100644 index 0000000000..8e67feb4cf --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs @@ -0,0 +1,109 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(ChangePasswordModel<>))] + public abstract class ChangePasswordModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class ChangePasswordModel : ChangePasswordModel where TUser : class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public ChangePasswordModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) + { + return RedirectToPage("./SetPassword"); + } + + return Page(); + } + + public override async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + foreach (var error in changePasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogInformation("User changed their password successfully."); + StatusMessage = "Your password has been changed."; + + return RedirectToPage(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml new file mode 100644 index 0000000000..eb1bc438ec --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml @@ -0,0 +1,34 @@ +@page +@model DeletePersonalDataModel +@{ + ViewData["Title"] = "Delete Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ + + +
+
+
+ @if (Model.RequirePassword) + { +
+ + + +
+ } + +
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 0000000000..7d0dbf4c18 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,93 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(DeletePersonalDataModel<>))] + public abstract class DeletePersonalDataModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + + public bool RequirePassword { get; set; } + + public virtual Task OnGet() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class DeletePersonalDataModel : DeletePersonalDataModel where TUser: class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public DeletePersonalDataModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + if (RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Password not correct."); + return Page(); + } + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'."); + } + + await _signInManager.SignOutAsync(); + + _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + return Redirect("~/"); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml new file mode 100644 index 0000000000..e2126672e2 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml @@ -0,0 +1,26 @@ +@page +@model Disable2faModel +@{ + ViewData["Title"] = "Disable two-factor authentication (2FA)"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ + + +
+
+ +
+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs new file mode 100644 index 0000000000..be9493db40 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(Disable2faModel<>))] + public abstract class Disable2faModel : PageModel + { + [TempData] + public string StatusMessage { get; set; } + + public virtual Task OnGet() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class Disable2faModel : Disable2faModel where TUser : class + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public Disable2faModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public override async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!await _userManager.GetTwoFactorEnabledAsync(user)) + { + throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); + } + + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); + StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; + return RedirectToPage("./TwoFactorAuthentication"); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml new file mode 100644 index 0000000000..fa300e7b38 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml @@ -0,0 +1,12 @@ +@page +@model DownloadPersonalDataModel +@{ + ViewData["Title"] = "Download Your Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs new file mode 100644 index 0000000000..827c8c02b4 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(DownloadPersonalDataModel<>))] + public abstract class DownloadPersonalDataModel : PageModel + { + public virtual IActionResult OnGet() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class DownloadPersonalDataModel : DownloadPersonalDataModel where TUser : class + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public DownloadPersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public override IActionResult OnGet() + { + return NotFound(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); + + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(TUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) + { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } + + var logins = await _userManager.GetLoginsAsync(user); + foreach (var l in logins) + { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } + + personalData.Add($"Authenticator Key", await _userManager.GetAuthenticatorKeyAsync(user)); + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json"); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml new file mode 100644 index 0000000000..67debf1fc9 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml @@ -0,0 +1,54 @@ +@page +@model EnableAuthenticatorModel +@{ + ViewData["Title"] = "Configure authenticator app"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+
+

To use an authenticator app go through the following steps:

+
    +
  1. +

    + Download a two-factor authenticator app like Microsoft Authenticator for + Windows Phone, + Android and + iOS or + Google Authenticator for + Android and + iOS. +

    +
  2. +
  3. +

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    +
    To enable QR code generation please read our documentation.
    +
    +
    +
  4. +
  5. +

    + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. +

    +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
  6. +
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs new file mode 100644 index 0000000000..5e5bb89445 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs @@ -0,0 +1,164 @@ +// 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.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(EnableAuthenticatorModel<>))] + public class EnableAuthenticatorModel : PageModel + { + public string SharedKey { get; set; } + + public string AuthenticatorUri { get; set; } + + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class EnableAuthenticatorModel : EnableAuthenticatorModel where TUser : class + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + private readonly UrlEncoder _urlEncoder; + + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + public EnableAuthenticatorModel( + UserManager userManager, + ILogger logger, + UrlEncoder urlEncoder) + { + _userManager = userManager; + _logger = logger; + _urlEncoder = urlEncoder; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadSharedKeyAndQrCodeUriAsync(user); + + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + // Strip spaces and hypens + var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( + user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + ModelState.AddModelError("Input.Code", "Verification code is invalid."); + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + await _userManager.SetTwoFactorEnabledAsync(user, true); + var userId = await _userManager.GetUserIdAsync(user); + _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + StatusMessage = "Your authenticator app has been verified."; + + if (await _userManager.CountRecoveryCodesAsync(user) == 0) + { + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + return RedirectToPage("./ShowRecoveryCodes"); + } + else + { + return RedirectToPage("./TwoFactorAuthentication"); + } + } + + private async Task LoadSharedKeyAndQrCodeUriAsync(TUser user) + { + // Load the authenticator key & QR code URI to display on the form + var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } + + SharedKey = FormatKey(unformattedKey); + + var email = await _userManager.GetEmailAsync(user); + AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); + currentPosition += 4; + } + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + AuthenticatorUriFormat, + _urlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"), + _urlEncoder.Encode(email), + unformattedKey); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml new file mode 100644 index 0000000000..f1992da2d4 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml @@ -0,0 +1,53 @@ +@page +@model ExternalLoginsModel +@{ + ViewData["Title"] = "Manage your external logins"; + ViewData["ActivePage"] = ManageNavPages.ExternalLogins; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +@if (Model.CurrentLogins?.Count > 0) +{ +

Registered Logins

+ + + @foreach (var login in Model.CurrentLogins) + { + + + + + } + +
@login.LoginProvider + @if (Model.ShowRemoveButton) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins?.Count > 0) +{ +

Add another service to log in.

+
+ +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs new file mode 100644 index 0000000000..ee3df64262 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs @@ -0,0 +1,134 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(ExternalLoginsModel<>))] + public abstract class ExternalLoginsModel : PageModel + { + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + + public bool ShowRemoveButton { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) => throw new NotImplementedException(); + + public virtual Task OnPostLinkLoginAsync(string provider) => throw new NotImplementedException(); + + public virtual Task OnGetLinkLoginCallbackAsync() => throw new NotImplementedException(); + } + + internal class ExternalLoginsModel : ExternalLoginsModel where TUser : class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IUserStore _userStore; + + public ExternalLoginsModel( + UserManager userManager, + SignInManager signInManager, + IUserStore userStore) + { + _userManager = userManager; + _signInManager = signInManager; + _userStore = userStore; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + CurrentLogins = await _userManager.GetLoginsAsync(user); + OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + + string passwordHash = null; + if (_userStore is IUserPasswordStore userPasswordStore) + { + passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted); + } + + ShowRemoveButton = passwordHash != null || CurrentLogins.Count > 1; + return Page(); + } + + public override async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); + if (!result.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{userId}'."); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "The external login was removed."; + return RedirectToPage(); + } + + public override async Task OnPostLinkLoginAsync(string provider) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback"); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + return new ChallengeResult(provider, properties); + } + + public override async Task OnGetLinkLoginCallbackAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var userId = await _userManager.GetUserIdAsync(user); + var info = await _signInManager.GetExternalLoginInfoAsync(userId); + if (info == null) + { + throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{userId}'."); + } + + var result = await _userManager.AddLoginAsync(user, info); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred adding external login for user with ID '{userId}'."); + } + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + StatusMessage = "The external login was added."; + return RedirectToPage(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml new file mode 100644 index 0000000000..ffc8d678ef --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml @@ -0,0 +1,27 @@ +@page +@model GenerateRecoveryCodesModel +@{ + ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ +
+
+ +
+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs new file mode 100644 index 0000000000..434948c3b8 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs @@ -0,0 +1,81 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(GenerateRecoveryCodesModel<>))] + public abstract class GenerateRecoveryCodesModel : PageModel + { + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class GenerateRecoveryCodesModel : GenerateRecoveryCodesModel where TUser : class + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public GenerateRecoveryCodesModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + if (!isTwoFactorEnabled) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); + } + + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!isTwoFactorEnabled) + { + throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); + } + + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + + _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); + StatusMessage = "You have generated new recovery codes."; + return RedirectToPage("./ShowRecoveryCodes"); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml new file mode 100644 index 0000000000..cf71af3425 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -0,0 +1,46 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Profile"; + ViewData["ActivePage"] = ManageNavPages.Index; +} + +

@ViewData["Title"]

+@Html.Partial("_StatusMessage", Model.StatusMessage) +
+
+
+
+
+ + +
+
+ + @if (Model.IsEmailConfirmed) + { +
+ + +
+ } + else + { + + + } + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 0000000000..d7ad2f1ddc --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,165 @@ +// 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.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(IndexModel<>))] + public abstract class IndexModel : PageModel + { + public string Username { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + + public virtual Task OnPostSendVerificationEmailAsync() => throw new NotImplementedException(); + } + + internal class IndexModel : IndexModel where TUser : class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + + public IndexModel( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + var userName = await _userManager.GetUserNameAsync(user); + var email = await _userManager.GetEmailAsync(user); + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + + Username = userName; + + Input = new InputModel + { + Email = email, + PhoneNumber = phoneNumber + }; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + + return Page(); + } + + public override async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var email = await _userManager.GetEmailAsync(user); + if (Input.Email != email) + { + var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); + if (!setEmailResult.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'."); + } + + // In our UI email and user name are one and the same, so when we update the email + // we need to update the user name. + var setUserNameResult = await _userManager.SetUserNameAsync(user, Input.Email); + if (!setUserNameResult.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred setting name for user with ID '{userId}'."); + } + } + + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'."); + } + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + return RedirectToPage(); + } + + public override async Task OnPostSendVerificationEmailAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + + var userId = await _userManager.GetUserIdAsync(user); + var email = await _userManager.GetEmailAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = userId, code = code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + email, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs new file mode 100644 index 0000000000..5f44b7fe95 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + public static class ManageNavPages + { + public static string Index => "Index"; + + public static string ChangePassword => "ChangePassword"; + + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + + public static string ExternalLogins => "ExternalLogins"; + + public static string PersonalData => "PersonalData"; + + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + + public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); + + public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); + + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); + + public static string PageNavClass(ViewContext viewContext, string page) + { + var activePage = viewContext.ViewData["ActivePage"] as string + ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml new file mode 100644 index 0000000000..a35ee9b068 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml @@ -0,0 +1,27 @@ +@page +@model PersonalDataModel +@{ + ViewData["Title"] = "Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +
+
+

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ +
+

+ Delete +

+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs new file mode 100644 index 0000000000..ebe50d4aae --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs @@ -0,0 +1,42 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(PersonalDataModel<>))] + public abstract class PersonalDataModel : PageModel + { + public virtual Task OnGet() => throw new NotImplementedException(); + } + + internal class PersonalDataModel : PersonalDataModel where TUser : class + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public PersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public override async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml new file mode 100644 index 0000000000..334754cfa4 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml @@ -0,0 +1,24 @@ +@page +@model ResetAuthenticatorModel +@{ + ViewData["Title"] = "Reset authenticator key"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ +
+
+ +
+
\ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs new file mode 100644 index 0000000000..1df3621166 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(ResetAuthenticatorModel<>))] + public abstract class ResetAuthenticatorModel : PageModel + { + [TempData] + public string StatusMessage { get; set; } + + public virtual Task OnGet() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class ResetAuthenticatorModel : ResetAuthenticatorModel where TUser : class + { + UserManager _userManager; + private readonly SignInManager _signInManager; + ILogger _logger; + + public ResetAuthenticatorModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _userManager.ResetAuthenticatorKeyAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", userId); + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; + + return RedirectToPage("./EnableAuthenticator"); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml new file mode 100644 index 0000000000..02d35209f3 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml @@ -0,0 +1,35 @@ +@page +@model SetPasswordModel +@{ + ViewData["Title"] = "Set password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

Set your password

+@Html.Partial("_StatusMessage", Model.StatusMessage) +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+
+
+
+
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs new file mode 100644 index 0000000000..9424203802 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs @@ -0,0 +1,100 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(SetPasswordModel<>))] + public abstract class SetPasswordModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class SetPasswordModel : SetPasswordModel where TUser : class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public SetPasswordModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + + if (hasPassword) + { + return RedirectToPage("./ChangePassword"); + } + + return Page(); + } + + public override async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); + if (!addPasswordResult.Succeeded) + { + foreach (var error in addPasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your password has been set."; + + return RedirectToPage(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml new file mode 100644 index 0000000000..72d4a7025b --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml @@ -0,0 +1,26 @@ +@page +@model ShowRecoveryCodesModel +@{ + ViewData["Title"] = "Recovery codes"; + ViewData["ActivePage"] = "TwoFactorAuthentication"; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ +
+
+ @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) + { + @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
+ } +
+
diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs new file mode 100644 index 0000000000..677ec1af7a --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs @@ -0,0 +1,27 @@ +// 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.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + public class ShowRecoveryCodesModel : PageModel + { + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public IActionResult OnGet() + { + if (RecoveryCodes == null || RecoveryCodes.Length == 0) + { + return RedirectToPage("./TwoFactorAuthentication"); + } + + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml new file mode 100644 index 0000000000..bfd2791584 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml @@ -0,0 +1,57 @@ +@page +@model TwoFactorAuthenticationModel +@{ + ViewData["Title"] = "Two-factor authentication (2FA)"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+@if (Model.Is2faEnabled) +{ + if (Model.RecoveryCodesLeft == 0) + { +
+ You have no recovery codes left. +

You must generate a new set of recovery codes before you can log in with a recovery code.

+
+ } + else if (Model.RecoveryCodesLeft == 1) + { +
+ You have 1 recovery code left. +

You can generate a new set of recovery codes.

+
+ } + else if (Model.RecoveryCodesLeft <= 3) + { +
+ You have @Model.RecoveryCodesLeft recovery codes left. +

You should generate a new set of recovery codes.

+
+ } + + if (Model.IsMachineRemembered) + { +
+ +
+ } + Disable 2FA + Reset recovery codes +} + +
Authenticator app
+@if (!Model.HasAuthenticator) +{ + Add authenticator app +} +else +{ + Setup authenticator app + Reset authenticator app +} + +@section Scripts { + +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs new file mode 100644 index 0000000000..7c471aac57 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs @@ -0,0 +1,76 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal +{ + [IdentityDefaultUI(typeof(TwoFactorAuthenticationModel<>))] + public abstract class TwoFactorAuthenticationModel : PageModel + { + public bool HasAuthenticator { get; set; } + + public int RecoveryCodesLeft { get; set; } + + [BindProperty] + public bool Is2faEnabled { get; set; } + + public bool IsMachineRemembered { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public virtual Task OnGetAsync() => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + + } + + internal class TwoFactorAuthenticationModel : TwoFactorAuthenticationModel where TUser : class + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public TwoFactorAuthenticationModel( + UserManager userManager, SignInManager signInManager, ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public override async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; + Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); + RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); + + return Page(); + } + + public override async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await _signInManager.ForgetTwoFactorClientAsync(); + StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; + return RedirectToPage(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_Layout.cshtml new file mode 100644 index 0000000000..ea1687fdd5 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_Layout.cshtml @@ -0,0 +1,30 @@ +@{ + if (ViewData.TryGetValue("ParentLayout", out var parentLayout)) + { + Layout = (string)parentLayout; + } + else + { + Layout = "/Areas/Identity/Pages/_Layout.cshtml"; + } +} + +

Manage your account

+ +
+

Change your account settings

+
+
+
+ +
+
+ @RenderBody() +
+
+
+ +@section Scripts { + @RenderSection("Scripts", required: false) +} + diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml new file mode 100644 index 0000000000..6e42d16d67 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -0,0 +1,11 @@ + + diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml new file mode 100644 index 0000000000..e996841309 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml new file mode 100644 index 0000000000..25a8674a74 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml @@ -0,0 +1 @@ +@using Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewStart.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewStart.cshtml new file mode 100644 index 0000000000..4afb326bcc --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Manage/_ViewStart.cshtml @@ -0,0 +1,7 @@ +@{ + if (Layout != "_Layout") + { + ViewData["ParentLayout"] = Layout; + Layout = "./_Layout.cshtml"; + } +} \ No newline at end of file diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml new file mode 100644 index 0000000000..d40f55c35c --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml @@ -0,0 +1,37 @@ +@page +@model RegisterModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+ +
+
+
+

Create a new account.

+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs new file mode 100644 index 0000000000..d431b0d0b2 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -0,0 +1,141 @@ +// 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.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(RegisterModel<>))] + public abstract class RegisterModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public virtual void OnGet(string returnUrl = null) => throw new NotImplementedException(); + + public virtual Task OnPostAsync(string returnUrl = null) => throw new NotImplementedException(); + } + + internal class RegisterModel : RegisterModel where TUser : class + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly IUserStore _userStore; + private readonly IUserEmailStore _emailStore; + private readonly ILogger _logger; + private readonly IEmailSender _emailSender; + + public RegisterModel( + UserManager userManager, + IUserStore userStore, + SignInManager signInManager, + ILogger logger, + IEmailSender emailSender) + { + _userManager = userManager; + _userStore = userStore; + _emailStore = GetEmailStore(); + _signInManager = signInManager; + _logger = logger; + _emailSender = emailSender; + } + + public override void OnGet(string returnUrl = null) + { + ReturnUrl = returnUrl; + } + + public override async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if (ModelState.IsValid) + { + var user = CreateUser(); + + await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + var result = await _userManager.CreateAsync(user, Input.Password); + + if (result.Succeeded) + { + _logger.LogInformation("User created a new account with password."); + + var userId = await _userManager.GetUserIdAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = userId, code = code }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + await _signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + + private TUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(TUser)}'. " + + $"Ensure that '{nameof(TUser)}' is not an abstract class and has a parameterless constructor, or alternatively " + + $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml"); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!_userManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)_userStore; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml new file mode 100644 index 0000000000..5ccb61edcf --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml @@ -0,0 +1,37 @@ +@page +@model ResetPasswordModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"]

+

Reset your password.

+
+
+
+
+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 0000000000..990eb81524 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,97 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + [IdentityDefaultUI(typeof(ResetPasswordModel<>))] + public abstract class ResetPasswordModel : PageModel + { + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + [Required] + public string Code { get; set; } + + } + public virtual IActionResult OnGet(string code = null) => throw new NotImplementedException(); + + public virtual Task OnPostAsync() => throw new NotImplementedException(); + } + + internal class ResetPasswordModel : ResetPasswordModel where TUser : class + { + private readonly UserManager _userManager; + + public ResetPasswordModel(UserManager userManager) + { + _userManager = userManager; + } + + public override IActionResult OnGet(string code = null) + { + if (code == null) + { + return BadRequest("A code must be supplied for password reset."); + } + else + { + Input = new InputModel + { + Code = code + }; + return Page(); + } + } + + public override async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToPage("./ResetPasswordConfirmation"); + } + + var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + return RedirectToPage("./ResetPasswordConfirmation"); + } + + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 0000000000..a9972d4311 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,10 @@ +@page +@model ResetPasswordConfirmationModel +@{ + ViewData["Title"] = "Reset password confirmation"; +} + +

@ViewData["Title"]

+

+ Your password has been reset. Please click here to log in. +

diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs new file mode 100644 index 0000000000..ef48687003 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs @@ -0,0 +1,16 @@ +// 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.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal +{ + [AllowAnonymous] + public class ResetPasswordConfirmationModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Account/_ViewImports.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Account/_ViewImports.cshtml new file mode 100644 index 0000000000..cb96533889 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Account/_ViewImports.cshtml @@ -0,0 +1 @@ +@using Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml new file mode 100644 index 0000000000..b1f3143a42 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml @@ -0,0 +1,23 @@ +@page +@model ErrorModel +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

diff --git a/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml.cs b/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml.cs new file mode 100644 index 0000000000..4be543c0b8 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/Error.cshtml.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Microsoft.AspNetCore.Identity.UI.Pages +{ + [AllowAnonymous] + public class ErrorModel : PageModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Pages/_Layout.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/_Layout.cshtml new file mode 100644 index 0000000000..a145bd6e13 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/_Layout.cshtml @@ -0,0 +1,83 @@ +@using Microsoft.AspNetCore.Hosting +@using Microsoft.AspNetCore.Mvc.ViewEngines +@inject IHostingEnvironment Environment +@inject ICompositeViewEngine Engine + + + + + + @ViewData["Title"] - @Environment.ApplicationName + + + + + + + + + + + + +
+ @RenderBody() +
+
+

© 2017 - @Environment.ApplicationName

+
+
+ + + + + + + + + + + + + @RenderSection("Scripts", required: false) + + diff --git a/src/Identity/src/UI/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000000..bacc0ae43b --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/src/Identity/src/UI/Areas/Identity/Pages/_ViewImports.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/_ViewImports.cshtml new file mode 100644 index 0000000000..121b48a914 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@namespace Microsoft.AspNetCore.Identity.UI.Pages.Internal +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Identity/src/UI/Areas/Identity/Pages/_ViewStart.cshtml b/src/Identity/src/UI/Areas/Identity/Pages/_ViewStart.cshtml new file mode 100644 index 0000000000..a5f10045db --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Pages/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/src/Identity/src/UI/Areas/Identity/Services/EmailSender.cs b/src/Identity/src/UI/Areas/Identity/Services/EmailSender.cs new file mode 100644 index 0000000000..33ef0f0cac --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Services/EmailSender.cs @@ -0,0 +1,15 @@ +// 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.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity.UI.Services +{ + internal class EmailSender : IEmailSender + { + public Task SendEmailAsync(string email, string subject, string htmlMessage) + { + return Task.CompletedTask; + } + } +} diff --git a/src/Identity/src/UI/Areas/Identity/Services/IEmailSender.cs b/src/Identity/src/UI/Areas/Identity/Services/IEmailSender.cs new file mode 100644 index 0000000000..5cc574f258 --- /dev/null +++ b/src/Identity/src/UI/Areas/Identity/Services/IEmailSender.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 System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Identity.UI.Services +{ + public interface IEmailSender + { + Task SendEmailAsync(string email, string subject, string htmlMessage); + } +} diff --git a/src/Identity/src/UI/IdentityBuilderUIExtensions.cs b/src/Identity/src/UI/IdentityBuilderUIExtensions.cs new file mode 100644 index 0000000000..398a4101eb --- /dev/null +++ b/src/Identity/src/UI/IdentityBuilderUIExtensions.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 Microsoft.AspNetCore.Identity.UI; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.AspNetCore.Identity +{ + /// + /// Default UI extensions to . + /// + public static class IdentityBuilderUIExtensions + { + /// + /// Adds a default, self-contained UI for Identity to the application using + /// Razor Pages in an area named Identity. + /// + /// + /// In order to use the default UI, the application must be using , + /// and contain a _LoginPartial partial view that + /// can be found by the application. + /// + /// The . + /// The . + public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) + { + builder.AddSignInManager(); + AddRelatedParts(builder); + + builder.Services.ConfigureOptions( + typeof(IdentityDefaultUIConfigureOptions<>) + .MakeGenericType(builder.UserType)); + builder.Services.TryAddTransient(); + + return builder; + } + + private static void AddRelatedParts(IdentityBuilder builder) + { + // For preview1, we don't have a good mechanism to plug in additional parts. + // We need to provide API surface to allow libraries to plug in existing parts + // that were optionally added. + // Challenges here are: + // * Discovery of the parts. + // * Ordering of the parts. + // * Loading of the assembly in memory. + + var mvcBuilder = builder.Services + .AddMvc() + .ConfigureApplicationPartManager(partManager => + { + var thisAssembly = typeof(IdentityBuilderUIExtensions).Assembly; + var relatedAssemblies = RelatedAssemblyAttribute.GetRelatedAssemblies(thisAssembly, throwOnError: true); + var relatedParts = relatedAssemblies.SelectMany(CompiledRazorAssemblyApplicationPartFactory.GetDefaultApplicationParts); + + foreach (var part in relatedParts) + { + partManager.ApplicationParts.Add(part); + } + }); + } + } +} diff --git a/src/Identity/src/UI/IdentityDefaultUIAttribute.cs b/src/Identity/src/UI/IdentityDefaultUIAttribute.cs new file mode 100644 index 0000000000..5e1cc3885b --- /dev/null +++ b/src/Identity/src/UI/IdentityDefaultUIAttribute.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity.UI +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + internal sealed class IdentityDefaultUIAttribute : Attribute + { + public IdentityDefaultUIAttribute(Type implementationTemplate) + { + Template = implementationTemplate; + } + + public Type Template { get; } + } +} diff --git a/src/Identity/src/UI/IdentityDefaultUIConfigureOptions.cs b/src/Identity/src/UI/IdentityDefaultUIConfigureOptions.cs new file mode 100644 index 0000000000..ff39626995 --- /dev/null +++ b/src/Identity/src/UI/IdentityDefaultUIConfigureOptions.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 Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity.UI.Areas.Identity.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Identity.UI +{ + internal class IdentityDefaultUIConfigureOptions : + IPostConfigureOptions, + IPostConfigureOptions, + IPostConfigureOptions where TUser : class + { + private const string IdentityUIDefaultAreaName = "Identity"; + + public IdentityDefaultUIConfigureOptions(IHostingEnvironment environment) + { + Environment = environment; + } + + public IHostingEnvironment Environment { get; } + + public void PostConfigure(string name, RazorPagesOptions options) + { + name = name ?? throw new ArgumentNullException(nameof(name)); + options = options ?? throw new ArgumentNullException(nameof(options)); + + options.AllowAreas = true; + options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage"); + options.Conventions.AuthorizeAreaPage(IdentityUIDefaultAreaName, "/Account/Logout"); + var convention = new IdentityPageModelConvention(); + options.Conventions.AddAreaFolderApplicationModelConvention( + IdentityUIDefaultAreaName, + "/", + pam => convention.Apply(pam)); + options.Conventions.AddAreaFolderApplicationModelConvention( + IdentityUIDefaultAreaName, + "/Account/Manage", + pam => pam.Filters.Add(new ExternalLoginsPageFilter())); + } + + public void PostConfigure(string name, StaticFileOptions options) + { + name = name ?? throw new ArgumentNullException(nameof(name)); + options = options ?? throw new ArgumentNullException(nameof(options)); + + // Basic initialization in case the options weren't initialized by any other component + options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider(); + if (options.FileProvider == null && Environment.WebRootFileProvider == null) + { + throw new InvalidOperationException("Missing FileProvider."); + } + + options.FileProvider = options.FileProvider ?? Environment.WebRootFileProvider; + + // Add our provider + var filesProvider = new ManifestEmbeddedFileProvider(GetType().Assembly, "wwwroot"); + options.FileProvider = new CompositeFileProvider(options.FileProvider, filesProvider); + } + + public void PostConfigure(string name, CookieAuthenticationOptions options) + { + name = name ?? throw new ArgumentNullException(nameof(name)); + options = options ?? throw new ArgumentNullException(nameof(options)); + + if (string.Equals(IdentityConstants.ApplicationScheme, name, StringComparison.Ordinal)) + { + options.LoginPath = $"/{IdentityUIDefaultAreaName}/Account/Login"; + options.LogoutPath = $"/{IdentityUIDefaultAreaName}/Account/Logout"; + options.AccessDeniedPath = $"/{IdentityUIDefaultAreaName}/Account/AccessDenied"; + } + } + } +} diff --git a/src/Identity/src/UI/IdentityPageModelConvention.cs b/src/Identity/src/UI/IdentityPageModelConvention.cs new file mode 100644 index 0000000000..79635db72c --- /dev/null +++ b/src/Identity/src/UI/IdentityPageModelConvention.cs @@ -0,0 +1,38 @@ +// 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.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace Microsoft.AspNetCore.Identity.UI +{ + internal class IdentityPageModelConvention : IPageApplicationModelConvention where TUser : class + { + public void Apply(PageApplicationModel model) + { + var defaultUIAttribute = model.ModelType.GetCustomAttribute(); + if (defaultUIAttribute == null) + { + return; + } + + ValidateTemplate(defaultUIAttribute.Template); + var templateInstance = defaultUIAttribute.Template.MakeGenericType(typeof(TUser)); + model.ModelType = templateInstance.GetTypeInfo(); + } + + private void ValidateTemplate(Type template) + { + if (template.IsAbstract || !template.IsGenericTypeDefinition) + { + throw new InvalidOperationException("Implementation type can't be abstract or non generic."); + } + var genericArguments = template.GetGenericArguments(); + if (genericArguments.Length != 1) + { + throw new InvalidOperationException("Implementation type contains wrong generic arity."); + } + } + } +} diff --git a/src/Identity/src/UI/IdentityServiceCollectionUIExtensions.cs b/src/Identity/src/UI/IdentityServiceCollectionUIExtensions.cs new file mode 100644 index 0000000000..d6254808c9 --- /dev/null +++ b/src/Identity/src/UI/IdentityServiceCollectionUIExtensions.cs @@ -0,0 +1,65 @@ +// 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.IO; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Default UI extensions to . + /// + public static class IdentityServiceCollectionUIExtensions + { + /// + /// Adds a set of common identity services to the application, including a default UI, token providers, + /// and configures authentication to use identity cookies. + /// + /// + /// In order to use the default UI, the application must be using , + /// and contain a _LoginPartial partial view that + /// can be found by the application. + /// + /// The . + /// The . + public static IdentityBuilder AddDefaultIdentity(this IServiceCollection services) where TUser : class + => services.AddDefaultIdentity(_ => { }); + + /// + /// Adds a set of common identity services to the application, including a default UI, token providers, + /// and configures authentication to use identity cookies. + /// + /// + /// In order to use the default UI, the application must be using , + /// and contain a _LoginPartial partial view that + /// can be found by the application. + /// + /// The . + /// Configures the . + /// The . + public static IdentityBuilder AddDefaultIdentity(this IServiceCollection services, Action configureOptions) where TUser : class + { + services.AddAuthentication(o => + { + o.DefaultScheme = IdentityConstants.ApplicationScheme; + o.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(o => { }); + + return services.AddIdentityCore(o => + { + o.Stores.MaxLengthForKeys = 128; + configureOptions?.Invoke(o); + }) + .AddDefaultUI() + .AddDefaultTokenProviders(); + } + } +} \ No newline at end of file diff --git a/src/Identity/src/UI/Microsoft.AspNetCore.Identity.UI.csproj b/src/Identity/src/UI/Microsoft.AspNetCore.Identity.UI.csproj new file mode 100644 index 0000000000..2795f6c76d --- /dev/null +++ b/src/Identity/src/UI/Microsoft.AspNetCore.Identity.UI.csproj @@ -0,0 +1,64 @@ + + + + ASP.NET Core Identity UI is the default Razor Pages built-in UI for the ASP.NET Core Identity framework. + Compiled Razor views assembly for the ASP.NET Core Identity UI package. + netstandard2.0 + false + aspnetcore;identity;membership;razorpages + true + Microsoft.AspNetCore.Mvc.ApplicationParts.NullApplicationPartFactory, Microsoft.AspNetCore.Mvc.Core + + + + + + + + + + + + + + + + + + + + + + + + + + <_parameter1>Microsoft.AspNetCore.Mvc.AdditionalReference + <_parameter2>$(RazorTargetName).dll,false + + + + <_Parameter1>BuildNumber + <_Parameter2>$(BuildNumber) + + + + <_Parameter1>BuildNumber + <_Parameter2>$(BuildNumber) + + + + + + + + + + + $(OutputPath)$(RazorTargetName).dll + + + + + + diff --git a/src/Identity/src/UI/THIRD-PARTY-NOTICES b/src/Identity/src/UI/THIRD-PARTY-NOTICES new file mode 100644 index 0000000000..7ec298c1e5 --- /dev/null +++ b/src/Identity/src/UI/THIRD-PARTY-NOTICES @@ -0,0 +1,193 @@ +.NET Core uses third-party libraries or other resources that may be +distributed under licenses different than the .NET Core software. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + dotnet@microsoft.com + +The attached notices are provided for information only. + +License notice for HTML5 Boilerplate +------------------------------------ + +"Copyright (c) HTML5 Boilerplate + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the ""Software""), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE." + +License notice for Normalize.css +-------------------------------- + +"# The MIT License (MIT) + +Copyright (c)Nicolas Gallagher and Jonathan Neal + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the ""Software""), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE." + +License notice for css-transitions +---------------------------------- + +Copyright (c) 2011 Alex MacCaw (info@eribium.org) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for micro-clearfix-hack +-------------------------------------- + +Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for Bootstrap v3.3.7 +----------------------------------- +The MIT License (MIT) + +Copyright (c) 2011-2016 Twitter, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +License notice for jQuery 3.3.1 +------------------------------- +Copyright JS Foundation and other contributors, https://js.foundation/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. + +License notice for jQuery Validation v1.17.0 +-------------------------------------------- +The MIT License (MIT) +===================== + +Copyright Jörn Zaefferer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/src/Identity/src/UI/baseline.netcore.json b/src/Identity/src/UI/baseline.netcore.json new file mode 100644 index 0000000000..8462b9d551 --- /dev/null +++ b/src/Identity/src/UI/baseline.netcore.json @@ -0,0 +1,176 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Identity.UI, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionUIExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddDefaultIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "AddDefaultIdentity", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TUser", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.IdentityBuilderUIExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddDefaultUI", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Identity.IdentityBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Identity.IdentityBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UI.Services.IEmailSender", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "SendEmailAsync", + "Parameters": [ + { + "Name": "email", + "Type": "System.String" + }, + { + "Name": "subject", + "Type": "System.String" + }, + { + "Name": "htmlMessage", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Identity.UI.Pages.ErrorModel", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Mvc.RazorPages.PageModel", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_RequestId", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequestId", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ShowRequestId", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "OnGet", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/css/site.css b/src/Identity/src/UI/wwwroot/Identity/css/site.css new file mode 100644 index 0000000000..6d0f6e44ec --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/css/site.css @@ -0,0 +1,35 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Wrapping element */ +/* Set some basic padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Carousel */ +.carousel-caption p { + font-size: 20px; + line-height: 1.4; +} + +/* Make .svg files in the carousel display properly in older browsers */ +.carousel-inner .item img[src$=".svg"] { + width: 100%; +} + +/* QR code generator */ +#qrCode { + margin: 15px; +} + +/* Hide/rearrange for smaller screens */ +@media screen and (max-width: 767px) { + /* Hide captions */ + .carousel-caption { + display: none; + } +} diff --git a/src/Identity/src/UI/wwwroot/Identity/css/site.min.css b/src/Identity/src/UI/wwwroot/Identity/css/site.min.css new file mode 100644 index 0000000000..5e93e30ae3 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/css/site.min.css @@ -0,0 +1 @@ +body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/js/site.js b/src/Identity/src/UI/wwwroot/Identity/js/site.js new file mode 100644 index 0000000000..0f3411a45b --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/js/site.js @@ -0,0 +1 @@ +// Write your JavaScript code. diff --git a/src/Identity/src/UI/wwwroot/Identity/js/site.min.js b/src/Identity/src/UI/wwwroot/Identity/js/site.min.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/.bower.json b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/.bower.json new file mode 100644 index 0000000000..1e99b62994 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/.bower.json @@ -0,0 +1,45 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "keywords": [ + "css", + "js", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "license": "MIT", + "moduleType": "globals", + "main": [ + "less/bootstrap.less", + "dist/js/bootstrap.js" + ], + "ignore": [ + "/.*", + "_config.yml", + "CNAME", + "composer.json", + "CONTRIBUTING.md", + "docs", + "js/tests", + "test-infra" + ], + "dependencies": { + "jquery": "1.9.1 - 3" + }, + "version": "3.3.7", + "_release": "3.3.7", + "_resolution": { + "type": "version", + "tag": "v3.3.7", + "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" + }, + "_source": "https://github.com/twbs/bootstrap.git", + "_target": "v3.3.7", + "_originalSource": "bootstrap", + "_direct": true +} \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/LICENSE b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/LICENSE new file mode 100644 index 0000000000..7a300022c3 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2016 Twitter, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css new file mode 100644 index 0000000000..31d8882661 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map new file mode 100644 index 0000000000..d876f60fb4 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css new file mode 100644 index 0000000000..5e39401957 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map new file mode 100644 index 0000000000..94813e9006 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css new file mode 100644 index 0000000000..6167622cec --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css @@ -0,0 +1,6757 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css.map b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css.map new file mode 100644 index 0000000000..f010c82d11 --- /dev/null +++ b/src/Identity/src/UI/wwwroot/Identity/lib/bootstrap/dist/css/bootstrap.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,iBAAA;CH8O9C;AG7OmC;EAAW,iBAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EEnDA,2CAAA;EACA,qBAAA;CNokCD;AIvgCD;EACE,UAAA;CJygCD;AIngCD;EACE,uBAAA;CJqgCD;AIjgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CP+kCD;AIrgCD;EACE,mBAAA;CJugCD;AIjgCD;EACE,aAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CP+lCD;AIjgCD;EACE,mBAAA;CJmgCD;AI7/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJ+/BD;AIv/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJy/BD;AIj/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJm/BH;AIx+BD;EACE,gBAAA;CJ0+BD;AQjoCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR6oCD;AQlpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRmqCH;AQ/pCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRoqCD;AQxqCD;;;;;;;;;;;;EAQI,eAAA;CR8qCH;AQ3qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRgrCD;AQprCD;;;;;;;;;;;;EAQI,eAAA;CR0rCH;AQtrCD;;EAAU,gBAAA;CR0rCT;AQzrCD;;EAAU,gBAAA;CR6rCT;AQ5rCD;;EAAU,gBAAA;CRgsCT;AQ/rCD;;EAAU,gBAAA;CRmsCT;AQlsCD;;EAAU,gBAAA;CRssCT;AQrsCD;;EAAU,gBAAA;CRysCT;AQnsCD;EACE,iBAAA;CRqsCD;AQlsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRosCD;AQ/rCD;EAwOA;IA1OI,gBAAA;GRqsCD;CACF;AQ7rCD;;EAEE,eAAA;CR+rCD;AQ5rCD;;EAEE,0BAAA;EACA,cAAA;CR8rCD;AQ1rCD;EAAuB,iBAAA;CR6rCtB;AQ5rCD;EAAuB,kBAAA;CR+rCtB;AQ9rCD;EAAuB,mBAAA;CRisCtB;AQhsCD;EAAuB,oBAAA;CRmsCtB;AQlsCD;EAAuB,oBAAA;CRqsCtB;AQlsCD;EAAuB,0BAAA;CRqsCtB;AQpsCD;EAAuB,0BAAA;CRusCtB;AQtsCD;EAAuB,2BAAA;CRysCtB;AQtsCD;EACE,eAAA;CRwsCD;AQtsCD;ECrGE,eAAA;CT8yCD;AS7yCC;;EAEE,eAAA;CT+yCH;AQ1sCD;ECxGE,eAAA;CTqzCD;ASpzCC;;EAEE,eAAA;CTszCH;AQ9sCD;EC3GE,eAAA;CT4zCD;AS3zCC;;EAEE,eAAA;CT6zCH;AQltCD;EC9GE,eAAA;CTm0CD;ASl0CC;;EAEE,eAAA;CTo0CH;AQttCD;ECjHE,eAAA;CT00CD;ASz0CC;;EAEE,eAAA;CT20CH;AQttCD;EAGE,YAAA;EE3HA,0BAAA;CVk1CD;AUj1CC;;EAEE,0BAAA;CVm1CH;AQxtCD;EE9HE,0BAAA;CVy1CD;AUx1CC;;EAEE,0BAAA;CV01CH;AQ5tCD;EEjIE,0BAAA;CVg2CD;AU/1CC;;EAEE,0BAAA;CVi2CH;AQhuCD;EEpIE,0BAAA;CVu2CD;AUt2CC;;EAEE,0BAAA;CVw2CH;AQpuCD;EEvIE,0BAAA;CV82CD;AU72CC;;EAEE,0BAAA;CV+2CH;AQnuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRquCD;AQ7tCD;;EAEE,cAAA;EACA,oBAAA;CR+tCD;AQluCD;;;;EAMI,iBAAA;CRkuCH;AQ3tCD;EACE,gBAAA;EACA,iBAAA;CR6tCD;AQztCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR4tCD;AQ9tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR4tCH;AQvtCD;EACE,cAAA;EACA,oBAAA;CRytCD;AQvtCD;;EAEE,wBAAA;CRytCD;AQvtCD;EACE,kBAAA;CRytCD;AQvtCD;EACE,eAAA;CRytCD;AQhsCD;EA6EA;IAvFM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXq6CC;EQ7nCH;IAhFM,mBAAA;GRgtCH;CACF;AQvsCD;;EAGE,aAAA;EACA,kCAAA;CRwsCD;AQtsCD;EACE,eAAA;EA9IqB,0BAAA;CRu1CtB;AQpsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRssCD;AQjsCG;;;EACE,iBAAA;CRqsCL;AQ/sCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRisCH;AQ/rCG;;;EACE,uBAAA;CRmsCL;AQ3rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR6rCD;AQvrCG;;;;;;EAAW,YAAA;CR+rCd;AQ9rCG;;;;;;EACE,uBAAA;CRqsCL;AQ/rCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRisCD;AYv+CD;;;;EAIE,+DAAA;CZy+CD;AYr+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZu+CD;AYn+CD;EACE,iBAAA;EACA,eAAA;EACA,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZq+CD;AY3+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZq+CH;AYh+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;CZk+CD;AY7+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZi+CH;AY59CD;EACE,kBAAA;EACA,mBAAA;CZ89CD;AaxhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd8hDD;AaxhDC;EAqEF;IAvEI,aAAA;Gb8hDD;CACF;Aa1hDC;EAkEF;IApEI,aAAA;GbgiDD;CACF;Aa5hDD;EA+DA;IAjEI,cAAA;GbkiDD;CACF;AazhDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdmjDD;AathDD;ECvBE,mBAAA;EACA,oBAAA;CdgjDD;AehjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfgjDL;AehiDG;EACE,YAAA;CfkiDL;Ae3hDC;EACE,YAAA;Cf6hDH;Ae9hDC;EACE,oBAAA;CfgiDH;AejiDC;EACE,oBAAA;CfmiDH;AepiDC;EACE,WAAA;CfsiDH;AeviDC;EACE,oBAAA;CfyiDH;Ae1iDC;EACE,oBAAA;Cf4iDH;Ae7iDC;EACE,WAAA;Cf+iDH;AehjDC;EACE,oBAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,WAAA;CfwjDH;AezjDC;EACE,oBAAA;Cf2jDH;Ae5jDC;EACE,mBAAA;Cf8jDH;AehjDC;EACE,YAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,oBAAA;CfwjDH;AezjDC;EACE,WAAA;Cf2jDH;Ae5jDC;EACE,oBAAA;Cf8jDH;Ae/jDC;EACE,oBAAA;CfikDH;AelkDC;EACE,WAAA;CfokDH;AerkDC;EACE,oBAAA;CfukDH;AexkDC;EACE,oBAAA;Cf0kDH;Ae3kDC;EACE,WAAA;Cf6kDH;Ae9kDC;EACE,oBAAA;CfglDH;AejlDC;EACE,mBAAA;CfmlDH;Ae/kDC;EACE,YAAA;CfilDH;AejmDC;EACE,WAAA;CfmmDH;AepmDC;EACE,mBAAA;CfsmDH;AevmDC;EACE,mBAAA;CfymDH;Ae1mDC;EACE,UAAA;Cf4mDH;Ae7mDC;EACE,mBAAA;Cf+mDH;AehnDC;EACE,mBAAA;CfknDH;AennDC;EACE,UAAA;CfqnDH;AetnDC;EACE,mBAAA;CfwnDH;AeznDC;EACE,mBAAA;Cf2nDH;Ae5nDC;EACE,UAAA;Cf8nDH;Ae/nDC;EACE,mBAAA;CfioDH;AeloDC;EACE,kBAAA;CfooDH;AehoDC;EACE,WAAA;CfkoDH;AepnDC;EACE,kBAAA;CfsnDH;AevnDC;EACE,0BAAA;CfynDH;Ae1nDC;EACE,0BAAA;Cf4nDH;Ae7nDC;EACE,iBAAA;Cf+nDH;AehoDC;EACE,0BAAA;CfkoDH;AenoDC;EACE,0BAAA;CfqoDH;AetoDC;EACE,iBAAA;CfwoDH;AezoDC;EACE,0BAAA;Cf2oDH;Ae5oDC;EACE,0BAAA;Cf8oDH;Ae/oDC;EACE,iBAAA;CfipDH;AelpDC;EACE,0BAAA;CfopDH;AerpDC;EACE,yBAAA;CfupDH;AexpDC;EACE,gBAAA;Cf0pDH;Aa1pDD;EElCI;IACE,YAAA;Gf+rDH;EexrDD;IACE,YAAA;Gf0rDD;Ee3rDD;IACE,oBAAA;Gf6rDD;Ee9rDD;IACE,oBAAA;GfgsDD;EejsDD;IACE,WAAA;GfmsDD;EepsDD;IACE,oBAAA;GfssDD;EevsDD;IACE,oBAAA;GfysDD;Ee1sDD;IACE,WAAA;Gf4sDD;Ee7sDD;IACE,oBAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,WAAA;GfqtDD;EettDD;IACE,oBAAA;GfwtDD;EeztDD;IACE,mBAAA;Gf2tDD;Ee7sDD;IACE,YAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,oBAAA;GfqtDD;EettDD;IACE,WAAA;GfwtDD;EeztDD;IACE,oBAAA;Gf2tDD;Ee5tDD;IACE,oBAAA;Gf8tDD;Ee/tDD;IACE,WAAA;GfiuDD;EeluDD;IACE,oBAAA;GfouDD;EeruDD;IACE,oBAAA;GfuuDD;EexuDD;IACE,WAAA;Gf0uDD;Ee3uDD;IACE,oBAAA;Gf6uDD;Ee9uDD;IACE,mBAAA;GfgvDD;Ee5uDD;IACE,YAAA;Gf8uDD;Ee9vDD;IACE,WAAA;GfgwDD;EejwDD;IACE,mBAAA;GfmwDD;EepwDD;IACE,mBAAA;GfswDD;EevwDD;IACE,UAAA;GfywDD;Ee1wDD;IACE,mBAAA;Gf4wDD;Ee7wDD;IACE,mBAAA;Gf+wDD;EehxDD;IACE,UAAA;GfkxDD;EenxDD;IACE,mBAAA;GfqxDD;EetxDD;IACE,mBAAA;GfwxDD;EezxDD;IACE,UAAA;Gf2xDD;Ee5xDD;IACE,mBAAA;Gf8xDD;Ee/xDD;IACE,kBAAA;GfiyDD;Ee7xDD;IACE,WAAA;Gf+xDD;EejxDD;IACE,kBAAA;GfmxDD;EepxDD;IACE,0BAAA;GfsxDD;EevxDD;IACE,0BAAA;GfyxDD;Ee1xDD;IACE,iBAAA;Gf4xDD;Ee7xDD;IACE,0BAAA;Gf+xDD;EehyDD;IACE,0BAAA;GfkyDD;EenyDD;IACE,iBAAA;GfqyDD;EetyDD;IACE,0BAAA;GfwyDD;EezyDD;IACE,0BAAA;Gf2yDD;Ee5yDD;IACE,iBAAA;Gf8yDD;Ee/yDD;IACE,0BAAA;GfizDD;EelzDD;IACE,yBAAA;GfozDD;EerzDD;IACE,gBAAA;GfuzDD;CACF;Aa/yDD;EE3CI;IACE,YAAA;Gf61DH;Eet1DD;IACE,YAAA;Gfw1DD;Eez1DD;IACE,oBAAA;Gf21DD;Ee51DD;IACE,oBAAA;Gf81DD;Ee/1DD;IACE,WAAA;Gfi2DD;Eel2DD;IACE,oBAAA;Gfo2DD;Eer2DD;IACE,oBAAA;Gfu2DD;Eex2DD;IACE,WAAA;Gf02DD;Ee32DD;IACE,oBAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,WAAA;Gfm3DD;Eep3DD;IACE,oBAAA;Gfs3DD;Eev3DD;IACE,mBAAA;Gfy3DD;Ee32DD;IACE,YAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,oBAAA;Gfm3DD;Eep3DD;IACE,WAAA;Gfs3DD;Eev3DD;IACE,oBAAA;Gfy3DD;Ee13DD;IACE,oBAAA;Gf43DD;Ee73DD;IACE,WAAA;Gf+3DD;Eeh4DD;IACE,oBAAA;Gfk4DD;Een4DD;IACE,oBAAA;Gfq4DD;Eet4DD;IACE,WAAA;Gfw4DD;Eez4DD;IACE,oBAAA;Gf24DD;Ee54DD;IACE,mBAAA;Gf84DD;Ee14DD;IACE,YAAA;Gf44DD;Ee55DD;IACE,WAAA;Gf85DD;Ee/5DD;IACE,mBAAA;Gfi6DD;Eel6DD;IACE,mBAAA;Gfo6DD;Eer6DD;IACE,UAAA;Gfu6DD;Eex6DD;IACE,mBAAA;Gf06DD;Ee36DD;IACE,mBAAA;Gf66DD;Ee96DD;IACE,UAAA;Gfg7DD;Eej7DD;IACE,mBAAA;Gfm7DD;Eep7DD;IACE,mBAAA;Gfs7DD;Eev7DD;IACE,UAAA;Gfy7DD;Ee17DD;IACE,mBAAA;Gf47DD;Ee77DD;IACE,kBAAA;Gf+7DD;Ee37DD;IACE,WAAA;Gf67DD;Ee/6DD;IACE,kBAAA;Gfi7DD;Eel7DD;IACE,0BAAA;Gfo7DD;Eer7DD;IACE,0BAAA;Gfu7DD;Eex7DD;IACE,iBAAA;Gf07DD;Ee37DD;IACE,0BAAA;Gf67DD;Ee97DD;IACE,0BAAA;Gfg8DD;Eej8DD;IACE,iBAAA;Gfm8DD;Eep8DD;IACE,0BAAA;Gfs8DD;Eev8DD;IACE,0BAAA;Gfy8DD;Ee18DD;IACE,iBAAA;Gf48DD;Ee78DD;IACE,0BAAA;Gf+8DD;Eeh9DD;IACE,yBAAA;Gfk9DD;Een9DD;IACE,gBAAA;Gfq9DD;CACF;Aa18DD;EE9CI;IACE,YAAA;Gf2/DH;Eep/DD;IACE,YAAA;Gfs/DD;Eev/DD;IACE,oBAAA;Gfy/DD;Ee1/DD;IACE,oBAAA;Gf4/DD;Ee7/DD;IACE,WAAA;Gf+/DD;EehgED;IACE,oBAAA;GfkgED;EengED;IACE,oBAAA;GfqgED;EetgED;IACE,WAAA;GfwgED;EezgED;IACE,oBAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,WAAA;GfihED;EelhED;IACE,oBAAA;GfohED;EerhED;IACE,mBAAA;GfuhED;EezgED;IACE,YAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,oBAAA;GfihED;EelhED;IACE,WAAA;GfohED;EerhED;IACE,oBAAA;GfuhED;EexhED;IACE,oBAAA;Gf0hED;Ee3hED;IACE,WAAA;Gf6hED;Ee9hED;IACE,oBAAA;GfgiED;EejiED;IACE,oBAAA;GfmiED;EepiED;IACE,WAAA;GfsiED;EeviED;IACE,oBAAA;GfyiED;Ee1iED;IACE,mBAAA;Gf4iED;EexiED;IACE,YAAA;Gf0iED;Ee1jED;IACE,WAAA;Gf4jED;Ee7jED;IACE,mBAAA;Gf+jED;EehkED;IACE,mBAAA;GfkkED;EenkED;IACE,UAAA;GfqkED;EetkED;IACE,mBAAA;GfwkED;EezkED;IACE,mBAAA;Gf2kED;Ee5kED;IACE,UAAA;Gf8kED;Ee/kED;IACE,mBAAA;GfilED;EellED;IACE,mBAAA;GfolED;EerlED;IACE,UAAA;GfulED;EexlED;IACE,mBAAA;Gf0lED;Ee3lED;IACE,kBAAA;Gf6lED;EezlED;IACE,WAAA;Gf2lED;Ee7kED;IACE,kBAAA;Gf+kED;EehlED;IACE,0BAAA;GfklED;EenlED;IACE,0BAAA;GfqlED;EetlED;IACE,iBAAA;GfwlED;EezlED;IACE,0BAAA;Gf2lED;Ee5lED;IACE,0BAAA;Gf8lED;Ee/lED;IACE,iBAAA;GfimED;EelmED;IACE,0BAAA;GfomED;EermED;IACE,0BAAA;GfumED;EexmED;IACE,iBAAA;Gf0mED;Ee3mED;IACE,0BAAA;Gf6mED;Ee9mED;IACE,yBAAA;GfgnED;EejnED;IACE,gBAAA;GfmnED;CACF;AgBvrED;EACE,8BAAA;ChByrED;AgBvrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChByrED;AgBvrED;EACE,iBAAA;ChByrED;AgBnrED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBqrED;AgBxrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,2BAAA;ChBqrEP;AgBnsED;EAoBI,uBAAA;EACA,8BAAA;ChBkrEH;AgBvsED;;;;;;EA8BQ,cAAA;ChBirEP;AgB/sED;EAoCI,2BAAA;ChB8qEH;AgBltED;EAyCI,uBAAA;ChB4qEH;AgBrqED;;;;;;EAOQ,aAAA;ChBsqEP;AgB3pED;EACE,uBAAA;ChB6pED;AgB9pED;;;;;;EAQQ,uBAAA;ChB8pEP;AgBtqED;;EAeM,yBAAA;ChB2pEL;AgBjpED;EAEI,0BAAA;ChBkpEH;AgBzoED;EAEI,0BAAA;ChB0oEH;AgBjoED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBmoED;AgB9nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBioEL;AiB7wEC;;;;;;;;;;;;EAOI,0BAAA;CjBoxEL;AiB9wEC;;;;;EAMI,0BAAA;CjB+wEL;AiBlyEC;;;;;;;;;;;;EAOI,0BAAA;CjByyEL;AiBnyEC;;;;;EAMI,0BAAA;CjBoyEL;AiBvzEC;;;;;;;;;;;;EAOI,0BAAA;CjB8zEL;AiBxzEC;;;;;EAMI,0BAAA;CjByzEL;AiB50EC;;;;;;;;;;;;EAOI,0BAAA;CjBm1EL;AiB70EC;;;;;EAMI,0BAAA;CjB80EL;AiBj2EC;;;;;;;;;;;;EAOI,0BAAA;CjBw2EL;AiBl2EC;;;;;EAMI,0BAAA;CjBm2EL;AgBjtED;EACE,iBAAA;EACA,kBAAA;ChBmtED;AgBtpED;EACA;IA3DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,uBAAA;GhBotED;EgB7pEH;IAnDM,iBAAA;GhBmtEH;EgBhqEH;;;;;;IA1CY,oBAAA;GhBktET;EgBxqEH;IAlCM,UAAA;GhB6sEH;EgB3qEH;;;;;;IAzBY,eAAA;GhB4sET;EgBnrEH;;;;;;IArBY,gBAAA;GhBgtET;EgB3rEH;;;;IARY,iBAAA;GhBysET;CACF;AkBn6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBk6ED;AkB/5ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBi6ED;AkB95ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBg6ED;AkBr5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL43ET;AkBr5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBu5ED;AkBp5ED;EACE,eAAA;ClBs5ED;AkBl5ED;EACE,eAAA;EACA,YAAA;ClBo5ED;AkBh5ED;;EAEE,aAAA;ClBk5ED;AkB94ED;;;EZrEE,2CAAA;EACA,qBAAA;CNw9ED;AkB74ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClB+4ED;AkBr3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CLwzET;AmBh8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CLy7ET;AKx5EC;EACE,YAAA;EACA,WAAA;CL05EH;AKx5EC;EAA0B,YAAA;CL25E3B;AK15EC;EAAgC,YAAA;CL65EjC;AkBj4EC;EACE,UAAA;EACA,8BAAA;ClBm4EH;AkB33EC;;;EAGE,0BAAA;EACA,WAAA;ClB63EH;AkB13EC;;EAEE,oBAAA;ClB43EH;AkBx3EC;EACE,aAAA;ClB03EH;AkB92ED;EACE,yBAAA;ClBg3ED;AkBx0ED;EAtBI;;;;IACE,kBAAA;GlBo2EH;EkBj2EC;;;;;;;;IAEE,kBAAA;GlBy2EH;EkBt2EC;;;;;;;;IAEE,kBAAA;GlB82EH;CACF;AkBp2ED;EACE,oBAAA;ClBs2ED;AkB91ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBg2ED;AkBr2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2EH;AkB91ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBg2ED;AkB71ED;;EAEE,iBAAA;ClB+1ED;AkB31ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClB61ED;AkB31ED;;EAEE,cAAA;EACA,kBAAA;ClB61ED;AkBp1EC;;;;;;EAGE,oBAAA;ClBy1EH;AkBn1EC;;;;EAEE,oBAAA;ClBu1EH;AkBj1EC;;;;EAGI,oBAAA;ClBo1EL;AkBz0ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClBy0ED;AkBv0EC;;EAEE,gBAAA;EACA,iBAAA;ClBy0EH;AkB5zED;ECnQE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBkkFD;AmBhkFC;EACE,aAAA;EACA,kBAAA;CnBkkFH;AmB/jFC;;EAEE,aAAA;CnBikFH;AkBx0ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClBy0EH;AkB/0ED;EASI,aAAA;EACA,kBAAA;ClBy0EH;AkBn1ED;;EAcI,aAAA;ClBy0EH;AkBv1ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClBy0EH;AkBr0ED;EC/RE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBumFD;AmBrmFC;EACE,aAAA;EACA,kBAAA;CnBumFH;AmBpmFC;;EAEE,aAAA;CnBsmFH;AkBj1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBk1EH;AkBx1ED;EASI,aAAA;EACA,kBAAA;ClBk1EH;AkB51ED;;EAcI,aAAA;ClBk1EH;AkBh2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBk1EH;AkBz0ED;EAEE,mBAAA;ClB00ED;AkB50ED;EAMI,sBAAA;ClBy0EH;AkBr0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBn0ED;;;;;;;;;;EC1ZI,eAAA;CnByuFH;AkB/0ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL0rFT;AmBxuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL+rFT;AkBz1ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBwuFH;AkB91ED;ECtYI,eAAA;CnBuuFH;AkB91ED;;;;;;;;;;EC7ZI,eAAA;CnBuwFH;AkB12ED;ECzZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwtFT;AmBtwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6tFT;AkBp3ED;EC/YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBswFH;AkBz3ED;ECzYI,eAAA;CnBqwFH;AkBz3ED;;;;;;;;;;EChaI,eAAA;CnBqyFH;AkBr4ED;EC5ZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLsvFT;AmBpyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2vFT;AkB/4ED;EClZI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBoyFH;AkBp5ED;EC5YI,eAAA;CnBmyFH;AkBh5EC;EACE,UAAA;ClBk5EH;AkBh5EC;EACE,OAAA;ClBk5EH;AkBx4ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB04ED;AkBvzED;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBy3EH;EkBrvEH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBu3EH;EkB1vEH;IAxHM,sBAAA;GlBq3EH;EkB7vEH;IApHM,sBAAA;IACA,uBAAA;GlBo3EH;EkBjwEH;;;IA9GQ,YAAA;GlBo3EL;EkBtwEH;IAxGM,YAAA;GlBi3EH;EkBzwEH;IApGM,iBAAA;IACA,uBAAA;GlBg3EH;EkB7wEH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB62EH;EkBpxEH;;IAtFQ,gBAAA;GlB82EL;EkBxxEH;;IAjFM,mBAAA;IACA,eAAA;GlB62EH;EkB7xEH;IA3EM,OAAA;GlB22EH;CACF;AkBj2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClB81EH;AkBz2ED;;EAiBI,iBAAA;ClB41EH;AkB72ED;EJthBE,mBAAA;EACA,oBAAA;Cds4FD;AkB10EC;EAyBF;IAnCM,kBAAA;IACA,iBAAA;IACA,iBAAA;GlBw1EH;CACF;AkBx3ED;EAwCI,YAAA;ClBm1EH;AkBr0EC;EAUF;IAdQ,kBAAA;IACA,gBAAA;GlB60EL;CACF;AkBn0EC;EAEF;IANQ,iBAAA;IACA,gBAAA;GlB20EL;CACF;AoBp6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC0CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB+JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL+tFT;AoBv6FG;;;;;;EdnBF,2CAAA;EACA,qBAAA;CNk8FD;AoB16FC;;;EAGE,YAAA;EACA,sBAAA;CpB46FH;AoBz6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLi5FT;AoBz6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CL05FT;AoBz6FG;;EAEE,qBAAA;CpB26FL;AoBl6FD;EC3DE,YAAA;EACA,uBAAA;EACA,mBAAA;CrBg+FD;AqB99FC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBs+FT;AqBn+FC;;;EAGE,uBAAA;CrBq+FH;AqBh+FG;;;;;;;;;EAGE,uBAAA;EACI,mBAAA;CrBw+FT;AoBv9FD;ECZI,YAAA;EACA,uBAAA;CrBs+FH;AoBx9FD;EC9DE,YAAA;EACA,0BAAA;EACA,sBAAA;CrByhGD;AqBvhGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB+hGT;AqB5hGC;;;EAGE,uBAAA;CrB8hGH;AqBzhGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBiiGT;AoB7gGD;ECfI,eAAA;EACA,uBAAA;CrB+hGH;AoB7gGD;EClEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBklGD;AqBhlGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBwlGT;AqBrlGC;;;EAGE,uBAAA;CrBulGH;AqBllGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB0lGT;AoBlkGD;ECnBI,eAAA;EACA,uBAAA;CrBwlGH;AoBlkGD;ECtEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB2oGD;AqBzoGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBipGT;AqB9oGC;;;EAGE,uBAAA;CrBgpGH;AqB3oGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBmpGT;AoBvnGD;ECvBI,eAAA;EACA,uBAAA;CrBipGH;AoBvnGD;EC1EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBosGD;AqBlsGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB0sGT;AqBvsGC;;;EAGE,uBAAA;CrBysGH;AqBpsGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB4sGT;AoB5qGD;EC3BI,eAAA;EACA,uBAAA;CrB0sGH;AoB5qGD;EC9EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB6vGD;AqB3vGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBmwGT;AqBhwGC;;;EAGE,uBAAA;CrBkwGH;AqB7vGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBqwGT;AoBjuGD;EC/BI,eAAA;EACA,uBAAA;CrBmwGH;AoB5tGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpB8tGD;AoB5tGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLkwGT;AoB7tGC;;;;EAIE,0BAAA;CpB+tGH;AoB7tGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpB+tGH;AoB3tGG;;;;EAEE,eAAA;EACA,sBAAA;CpB+tGL;AoBttGD;;ECxEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBkyGD;AoBztGD;;EC5EE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrByyGD;AoB5tGD;;EChFE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBgzGD;AoB3tGD;EACE,eAAA;EACA,YAAA;CpB6tGD;AoBztGD;EACE,gBAAA;CpB2tGD;AoBptGC;;;EACE,YAAA;CpBwtGH;AuBl3GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLisGT;AuBr3GC;EACE,WAAA;CvBu3GH;AuBn3GD;EACE,cAAA;CvBq3GD;AuBn3GC;EAAY,eAAA;CvBs3Gb;AuBr3GC;EAAY,mBAAA;CvBw3Gb;AuBv3GC;EAAY,yBAAA;CvB03Gb;AuBv3GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CL2sGT;AwBr5GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxBu5GD;AwBn5GD;;EAEE,mBAAA;CxBq5GD;AwBj5GD;EACE,WAAA;CxBm5GD;AwB/4GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBk5GD;AwB74GC;EACE,SAAA;EACA,WAAA;CxB+4GH;AwBx6GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBo8GD;AwB96GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB84GH;AwBx4GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB04GH;AwBp4GC;;;EAGE,YAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxBs4GH;AwB73GC;;;EAGE,eAAA;CxB+3GH;AwB33GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxB63GH;AwBx3GD;EAGI,eAAA;CxBw3GH;AwB33GD;EAQI,WAAA;CxBs3GH;AwB92GD;EACE,WAAA;EACA,SAAA;CxBg3GD;AwBx2GD;EACE,QAAA;EACA,YAAA;CxB02GD;AwBt2GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBw2GD;AwBp2GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxBs2GD;AwBl2GD;EACE,SAAA;EACA,WAAA;CxBo2GD;AwB51GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxB41GH;AwBn2GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxB41GH;AwBv0GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB05GC;EwBv1GD;IA1DA,QAAA;IACA,YAAA;GxBo5GC;CACF;A2BpiHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3BsiHD;A2B1iHD;;EAMI,mBAAA;EACA,YAAA;C3BwiHH;A2BtiHG;;;;;;;;EAIE,WAAA;C3B4iHL;A2BtiHD;;;;EAKI,kBAAA;C3BuiHH;A2BliHD;EACE,kBAAA;C3BoiHD;A2BriHD;;;EAOI,YAAA;C3BmiHH;A2B1iHD;;;EAYI,iBAAA;C3BmiHH;A2B/hHD;EACE,iBAAA;C3BiiHD;A2B7hHD;EACE,eAAA;C3B+hHD;A2B9hHC;EClDA,8BAAA;EACG,2BAAA;C5BmlHJ;A2B7hHD;;EC/CE,6BAAA;EACG,0BAAA;C5BglHJ;A2B5hHD;EACE,YAAA;C3B8hHD;A2B5hHD;EACE,iBAAA;C3B8hHD;A2B5hHD;;ECnEE,8BAAA;EACG,2BAAA;C5BmmHJ;A2B3hHD;ECjEE,6BAAA;EACG,0BAAA;C5B+lHJ;A2B1hHD;;EAEE,WAAA;C3B4hHD;A2B3gHD;EACE,kBAAA;EACA,mBAAA;C3B6gHD;A2B3gHD;EACE,mBAAA;EACA,oBAAA;C3B6gHD;A2BxgHD;EtB/CE,yDAAA;EACQ,iDAAA;CL0jHT;A2BxgHC;EtBnDA,yBAAA;EACQ,iBAAA;CL8jHT;A2BrgHD;EACE,eAAA;C3BugHD;A2BpgHD;EACE,wBAAA;EACA,uBAAA;C3BsgHD;A2BngHD;EACE,wBAAA;C3BqgHD;A2B9/GD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3B+/GH;A2BtgHD;EAcM,YAAA;C3B2/GL;A2BzgHD;;;;EAsBI,iBAAA;EACA,eAAA;C3By/GH;A2Bp/GC;EACE,iBAAA;C3Bs/GH;A2Bp/GC;EC3KA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B4pHF;A2Bt/GC;EC/KA,2BAAA;EACC,0BAAA;EAOD,gCAAA;EACC,+BAAA;C5BkqHF;A2Bv/GD;EACE,iBAAA;C3By/GD;A2Bv/GD;;EC/KE,8BAAA;EACC,6BAAA;C5B0qHF;A2Bt/GD;EC7LE,2BAAA;EACC,0BAAA;C5BsrHF;A2Bl/GD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3Bo/GD;A2Bx/GD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3Bq/GH;A2B9/GD;EAYI,YAAA;C3Bq/GH;A2BjgHD;EAgBI,WAAA;C3Bo/GH;A2Bn+GD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3Bo+GL;A6B9sHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BgtHD;A6B7sHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7B+sHH;A6BxtHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7BusHH;A6BrsHG;EACE,WAAA;C7BusHL;A6B7rHD;;;EV0BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwqHD;AmBtqHC;;;EACE,aAAA;EACA,kBAAA;CnB0qHH;AmBvqHC;;;;;;EAEE,aAAA;CnB6qHH;A6B/sHD;;;EVqBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+rHD;AmB7rHC;;;EACE,aAAA;EACA,kBAAA;CnBisHH;AmB9rHC;;;;;;EAEE,aAAA;CnBosHH;A6B7tHD;;;EAGE,oBAAA;C7B+tHD;A6B7tHC;;;EACE,iBAAA;C7BiuHH;A6B7tHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7B+tHD;A6B1tHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;C7B4tHD;A6BztHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6BztHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6B/uHD;;EA0BI,cAAA;C7BytHH;A6BptHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;C5Bi0HJ;A6BrtHD;EACE,gBAAA;C7ButHD;A6BrtHD;;;;;;;EDxGE,6BAAA;EACG,0BAAA;C5Bs0HJ;A6BttHD;EACE,eAAA;C7BwtHD;A6BntHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BmtHD;A6BxtHD;EAUI,mBAAA;C7BitHH;A6B3tHD;EAYM,kBAAA;C7BktHL;A6B/sHG;;;EAGE,WAAA;C7BitHL;A6B5sHC;;EAGI,mBAAA;C7B6sHL;A6B1sHC;;EAGI,WAAA;EACA,kBAAA;C7B2sHL;A8B12HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B42HD;A8B/2HD;EAOI,mBAAA;EACA,eAAA;C9B22HH;A8Bn3HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B22HL;A8B12HK;;EAEE,sBAAA;EACA,0BAAA;C9B42HP;A8Bv2HG;EACE,eAAA;C9By2HL;A8Bv2HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By2HP;A8Bl2HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo2HL;A8B74HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm5HD;A8Bn5HD;EA0DI,gBAAA;C9B41HH;A8Bn1HD;EACE,8BAAA;C9Bq1HD;A8Bt1HD;EAGI,YAAA;EAEA,oBAAA;C9Bq1HH;A8B11HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo1HL;A8Bn1HK;EACE,mCAAA;C9Bq1HP;A8B/0HK;;;EAGE,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,iCAAA;EACA,gBAAA;C9Bi1HP;A8B50HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6vHD;A8Bh1HC;EAwDE,YAAA;C9B2xHH;A8Bn1HC;EA0DI,mBAAA;EACA,mBAAA;C9B4xHL;A8Bv1HC;EAgEE,UAAA;EACA,WAAA;C9B0xHH;A8B9wHD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9ByxHH;E8BztHH;IA9DQ,iBAAA;G9B0xHL;CACF;A8Bp2HC;EAuFE,gBAAA;EACA,mBAAA;C9BgxHH;A8Bx2HC;;;EA8FE,uBAAA;C9B+wHH;A8BjwHD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9B8wHH;E8B3uHH;;;IA9BM,0BAAA;G9B8wHH;CACF;A8B/2HD;EAEI,YAAA;C9Bg3HH;A8Bl3HD;EAMM,mBAAA;C9B+2HL;A8Br3HD;EASM,iBAAA;C9B+2HL;A8B12HK;;;EAGE,YAAA;EACA,0BAAA;C9B42HP;A8Bp2HD;EAEI,YAAA;C9Bq2HH;A8Bv2HD;EAIM,gBAAA;EACA,eAAA;C9Bs2HL;A8B11HD;EACE,YAAA;C9B41HD;A8B71HD;EAII,YAAA;C9B41HH;A8Bh2HD;EAMM,mBAAA;EACA,mBAAA;C9B61HL;A8Bp2HD;EAYI,UAAA;EACA,WAAA;C9B21HH;A8B/0HD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B01HH;E8B1xHH;IA9DQ,iBAAA;G9B21HL;CACF;A8Bn1HD;EACE,iBAAA;C9Bq1HD;A8Bt1HD;EAKI,gBAAA;EACA,mBAAA;C9Bo1HH;A8B11HD;;;EAYI,uBAAA;C9Bm1HH;A8Br0HD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9Bk1HH;E8B/yHH;;;IA9BM,0BAAA;G9Bk1HH;CACF;A8Bz0HD;EAEI,cAAA;C9B00HH;A8B50HD;EAKI,eAAA;C9B00HH;A8Bj0HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8iIF;A+BxiID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0iID;A+BliID;EA8nBA;IAhoBI,mBAAA;G/BwiID;CACF;A+BzhID;EAgnBA;IAlnBI,YAAA;G/B+hID;CACF;A+BjhID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkhID;A+BhhIC;EACE,iBAAA;C/BkhIH;A+Bt/HD;EA6jBA;IArlBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkhID;E+BhhIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkhIH;E+B/gIC;IACE,oBAAA;G/BihIH;E+B5gIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8gIH;CACF;A+B1gID;;EAGI,kBAAA;C/B2gIH;A+BtgIC;EAmjBF;;IArjBM,kBAAA;G/B6gIH;CACF;A+BpgID;;;;EAII,oBAAA;EACA,mBAAA;C/BsgIH;A+BhgIC;EAgiBF;;;;IAniBM,gBAAA;IACA,eAAA;G/B0gIH;CACF;A+B9/HD;EACE,cAAA;EACA,sBAAA;C/BggID;A+B3/HD;EA8gBA;IAhhBI,iBAAA;G/BigID;CACF;A+B7/HD;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+/HD;A+Bz/HD;EAggBA;;IAlgBI,iBAAA;G/BggID;CACF;A+B9/HD;EACE,OAAA;EACA,sBAAA;C/BggID;A+B9/HD;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BggID;A+B1/HD;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4/HD;A+B1/HC;;EAEE,sBAAA;C/B4/HH;A+BrgID;EAaI,eAAA;C/B2/HH;A+Bl/HD;EALI;;IAEE,mBAAA;G/B0/HH;CACF;A+Bh/HD;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/Bm/HD;A+B/+HC;EACE,WAAA;C/Bi/HH;A+B//HD;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B++HH;A+BrgID;EAyBI,gBAAA;C/B++HH;A+Bz+HD;EAqbA;IAvbI,cAAA;G/B++HD;CACF;A+Bt+HD;EACE,oBAAA;C/Bw+HD;A+Bz+HD;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/Bw+HH;A+B58HC;EA2YF;IAjaM,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/Bs+HH;E+B3kHH;;IAxZQ,2BAAA;G/Bu+HL;E+B/kHH;IArZQ,kBAAA;G/Bu+HL;E+Bt+HK;;IAEE,uBAAA;G/Bw+HP;CACF;A+Bt9HD;EA+XA;IA1YI,YAAA;IACA,UAAA;G/Bq+HD;E+B5lHH;IAtYM,YAAA;G/Bq+HH;E+B/lHH;IApYQ,kBAAA;IACA,qBAAA;G/Bs+HL;CACF;A+B39HD;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4vID;AkBtuHD;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBwyHH;EkBpqHH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBsyHH;EkBzqHH;IAxHM,sBAAA;GlBoyHH;EkB5qHH;IApHM,sBAAA;IACA,uBAAA;GlBmyHH;EkBhrHH;;;IA9GQ,YAAA;GlBmyHL;EkBrrHH;IAxGM,YAAA;GlBgyHH;EkBxrHH;IApGM,iBAAA;IACA,uBAAA;GlB+xHH;EkB5rHH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB4xHH;EkBnsHH;;IAtFQ,gBAAA;GlB6xHL;EkBvsHH;;IAjFM,mBAAA;IACA,eAAA;GlB4xHH;EkB5sHH;IA3EM,OAAA;GlB0xHH;CACF;A+BpgIC;EAmWF;IAzWM,mBAAA;G/B8gIH;E+B5gIG;IACE,iBAAA;G/B8gIL;CACF;A+B7/HD;EAoVA;IA5VI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmwIP;CACF;A+BngID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B00IF;A+BngID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By0IF;A+B//HD;EChVE,gBAAA;EACA,mBAAA;ChCk1ID;A+BhgIC;ECnVA,iBAAA;EACA,oBAAA;ChCs1ID;A+BjgIC;ECtVA,iBAAA;EACA,oBAAA;ChC01ID;A+B3/HD;EChWE,iBAAA;EACA,oBAAA;ChC81ID;A+Bv/HD;EAsSA;IA1SI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+/HD;CACF;A+Bl+HD;EAhBE;IExWA,uBAAA;GjC81IC;E+Br/HD;IE5WA,wBAAA;IF8WE,oBAAA;G/Bu/HD;E+Bz/HD;IAKI,gBAAA;G/Bu/HH;CACF;A+B9+HD;EACE,0BAAA;EACA,sBAAA;C/Bg/HD;A+Bl/HD;EAKI,YAAA;C/Bg/HH;A+B/+HG;;EAEE,eAAA;EACA,8BAAA;C/Bi/HL;A+B1/HD;EAcI,YAAA;C/B++HH;A+B7/HD;EAmBM,YAAA;C/B6+HL;A+B3+HK;;EAEE,YAAA;EACA,8BAAA;C/B6+HP;A+Bz+HK;;;EAGE,YAAA;EACA,0BAAA;C/B2+HP;A+Bv+HK;;;EAGE,YAAA;EACA,8BAAA;C/By+HP;A+BjhID;EA8CI,mBAAA;C/Bs+HH;A+Br+HG;;EAEE,uBAAA;C/Bu+HL;A+BxhID;EAoDM,uBAAA;C/Bu+HL;A+B3hID;;EA0DI,sBAAA;C/Bq+HH;A+B99HK;;;EAGE,0BAAA;EACA,YAAA;C/Bg+HP;A+B/7HC;EAoKF;IA7LU,YAAA;G/B49HP;E+B39HO;;IAEE,YAAA;IACA,8BAAA;G/B69HT;E+Bz9HO;;;IAGE,YAAA;IACA,0BAAA;G/B29HT;E+Bv9HO;;;IAGE,YAAA;IACA,8BAAA;G/By9HT;CACF;A+B3jID;EA8GI,YAAA;C/Bg9HH;A+B/8HG;EACE,YAAA;C/Bi9HL;A+BjkID;EAqHI,YAAA;C/B+8HH;A+B98HG;;EAEE,YAAA;C/Bg9HL;A+B58HK;;;;EAEE,YAAA;C/Bg9HP;A+Bx8HD;EACE,uBAAA;EACA,sBAAA;C/B08HD;A+B58HD;EAKI,eAAA;C/B08HH;A+Bz8HG;;EAEE,YAAA;EACA,8BAAA;C/B28HL;A+Bp9HD;EAcI,eAAA;C/By8HH;A+Bv9HD;EAmBM,eAAA;C/Bu8HL;A+Br8HK;;EAEE,YAAA;EACA,8BAAA;C/Bu8HP;A+Bn8HK;;;EAGE,YAAA;EACA,0BAAA;C/Bq8HP;A+Bj8HK;;;EAGE,YAAA;EACA,8BAAA;C/Bm8HP;A+B3+HD;EA+CI,mBAAA;C/B+7HH;A+B97HG;;EAEE,uBAAA;C/Bg8HL;A+Bl/HD;EAqDM,uBAAA;C/Bg8HL;A+Br/HD;;EA2DI,sBAAA;C/B87HH;A+Bx7HK;;;EAGE,0BAAA;EACA,YAAA;C/B07HP;A+Bn5HC;EAwBF;IAvDU,sBAAA;G/Bs7HP;E+B/3HH;IApDU,0BAAA;G/Bs7HP;E+Bl4HH;IAjDU,eAAA;G/Bs7HP;E+Br7HO;;IAEE,YAAA;IACA,8BAAA;G/Bu7HT;E+Bn7HO;;;IAGE,YAAA;IACA,0BAAA;G/Bq7HT;E+Bj7HO;;;IAGE,YAAA;IACA,8BAAA;G/Bm7HT;CACF;A+B3hID;EA+GI,eAAA;C/B+6HH;A+B96HG;EACE,YAAA;C/Bg7HL;A+BjiID;EAsHI,eAAA;C/B86HH;A+B76HG;;EAEE,YAAA;C/B+6HL;A+B36HK;;;;EAEE,YAAA;C/B+6HP;AkCzjJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2jJD;AkChkJD;EAQI,sBAAA;ClC2jJH;AkCnkJD;EAWM,kBAAA;EACA,eAAA;EACA,YAAA;ClC2jJL;AkCxkJD;EAkBI,eAAA;ClCyjJH;AmC7kJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+kJD;AmCnlJD;EAOI,gBAAA;CnC+kJH;AmCtlJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,kBAAA;CnCglJL;AmC9kJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2lJJ;AmC7kJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwmJJ;AmCxkJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CnC4kJL;AmCtkJG;;;;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2kJL;AmCloJD;;;;;;EAkEM,eAAA;EACA,uBAAA;EACA,mBAAA;EACA,oBAAA;CnCwkJL;AmC/jJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8oJL;AoC5oJG;;ERKF,+BAAA;EACG,4BAAA;C5B2oJJ;AoC3oJG;;ERTF,gCAAA;EACG,6BAAA;C5BwpJJ;AmC1kJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8pJL;AoC5pJG;;ERKF,+BAAA;EACG,4BAAA;C5B2pJJ;AoC3pJG;;ERTF,gCAAA;EACG,6BAAA;C5BwqJJ;AqC3qJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6qJD;AqCjrJD;EAOI,gBAAA;CrC6qJH;AqCprJD;;EAUM,sBAAA;EACA,kBAAA;EACA,uBAAA;EACA,uBAAA;EACA,oBAAA;CrC8qJL;AqC5rJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6qJL;AqCjsJD;;EA2BM,aAAA;CrC0qJL;AqCrsJD;;EAkCM,YAAA;CrCuqJL;AqCzsJD;;;;EA2CM,eAAA;EACA,uBAAA;EACA,oBAAA;CrCoqJL;AsCltJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCotJD;AsChtJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CtCktJL;AsC7sJC;EACE,cAAA;CtC+sJH;AsC3sJC;EACE,mBAAA;EACA,UAAA;CtC6sJH;AsCtsJD;ECtCE,0BAAA;CvC+uJD;AuC5uJG;;EAEE,0BAAA;CvC8uJL;AsCzsJD;EC1CE,0BAAA;CvCsvJD;AuCnvJG;;EAEE,0BAAA;CvCqvJL;AsC5sJD;EC9CE,0BAAA;CvC6vJD;AuC1vJG;;EAEE,0BAAA;CvC4vJL;AsC/sJD;EClDE,0BAAA;CvCowJD;AuCjwJG;;EAEE,0BAAA;CvCmwJL;AsCltJD;ECtDE,0BAAA;CvC2wJD;AuCxwJG;;EAEE,0BAAA;CvC0wJL;AsCrtJD;EC1DE,0BAAA;CvCkxJD;AuC/wJG;;EAEE,0BAAA;CvCixJL;AwCnxJD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCqxJD;AwClxJC;EACE,cAAA;CxCoxJH;AwChxJC;EACE,mBAAA;EACA,UAAA;CxCkxJH;AwC/wJC;;EAEE,OAAA;EACA,iBAAA;CxCixJH;AwC5wJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CxC8wJL;AwCzwJC;;EAEE,eAAA;EACA,uBAAA;CxC2wJH;AwCxwJC;EACE,aAAA;CxC0wJH;AwCvwJC;EACE,kBAAA;CxCywJH;AwCtwJC;EACE,iBAAA;CxCwwJH;AyCl0JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo0JD;AyCz0JD;;EASI,eAAA;CzCo0JH;AyC70JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm0JH;AyCl1JD;EAmBI,0BAAA;CzCk0JH;AyC/zJC;;EAEE,mBAAA;EACA,mBAAA;EACA,oBAAA;CzCi0JH;AyC31JD;EA8BI,gBAAA;CzCg0JH;AyC9yJD;EACA;IAfI,kBAAA;IACA,qBAAA;GzCg0JD;EyC9zJC;;IAEE,mBAAA;IACA,oBAAA;GzCg0JH;EyCvzJH;;IAJM,gBAAA;GzC+zJH;CACF;A0C52JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL8rJT;A0Cx3JD;;EAaI,kBAAA;EACA,mBAAA;C1C+2JH;A0C32JC;;;EAGE,sBAAA;C1C62JH;A0Cl4JD;EA0BI,aAAA;EACA,eAAA;C1C22JH;A2Cp4JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Cs4JD;A2C14JD;EAQI,cAAA;EAEA,eAAA;C3Co4JH;A2C94JD;EAeI,kBAAA;C3Ck4JH;A2Cj5JD;;EAqBI,iBAAA;C3Cg4JH;A2Cr5JD;EAyBI,gBAAA;C3C+3JH;A2Cv3JD;;EAEE,oBAAA;C3Cy3JD;A2C33JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cy3JH;A2Cj3JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C26JD;A2Ct3JD;EClDI,0BAAA;C5C26JH;A2Cz3JD;EC/CI,eAAA;C5C26JH;A2Cx3JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cs7JD;A2C73JD;ECtDI,0BAAA;C5Cs7JH;A2Ch4JD;ECnDI,eAAA;C5Cs7JH;A2C/3JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Ci8JD;A2Cp4JD;EC1DI,0BAAA;C5Ci8JH;A2Cv4JD;ECvDI,eAAA;C5Ci8JH;A2Ct4JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C48JD;A2C34JD;EC9DI,0BAAA;C5C48JH;A2C94JD;EC3DI,eAAA;C5C48JH;A6C98JD;EACE;IAAQ,4BAAA;G7Ci9JP;E6Ch9JD;IAAQ,yBAAA;G7Cm9JP;CACF;A6Ch9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6Cx9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6C98JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CL26JT;A6C78JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL+zJT;A6C18JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C88JD;A6Cv8JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLu/JT;A6Cp8JD;EErEE,0BAAA;C/C4gKD;A+CzgKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C49JH;A6Cx8JD;EEzEE,0BAAA;C/CohKD;A+CjhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co+JH;A6C58JD;EE7EE,0BAAA;C/C4hKD;A+CzhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C4+JH;A6Ch9JD;EEjFE,0BAAA;C/CoiKD;A+CjiKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co/JH;AgD5iKD;EAEE,iBAAA;ChD6iKD;AgD3iKC;EACE,cAAA;ChD6iKH;AgDziKD;;EAEE,QAAA;EACA,iBAAA;ChD2iKD;AgDxiKD;EACE,eAAA;ChD0iKD;AgDviKD;EACE,eAAA;ChDyiKD;AgDtiKC;EACE,gBAAA;ChDwiKH;AgDpiKD;;EAEE,mBAAA;ChDsiKD;AgDniKD;;EAEE,oBAAA;ChDqiKD;AgDliKD;;;EAGE,oBAAA;EACA,oBAAA;ChDoiKD;AgDjiKD;EACE,uBAAA;ChDmiKD;AgDhiKD;EACE,uBAAA;ChDkiKD;AgD9hKD;EACE,cAAA;EACA,mBAAA;ChDgiKD;AgD1hKD;EACE,gBAAA;EACA,iBAAA;ChD4hKD;AiDnlKD;EAEE,oBAAA;EACA,gBAAA;CjDolKD;AiD5kKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,uBAAA;EACA,uBAAA;CjD6kKD;AiD1kKC;ErB3BA,6BAAA;EACC,4BAAA;C5BwmKF;AiD3kKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BqmKF;AiDpkKD;;EAEE,YAAA;CjDskKD;AiDxkKD;;EAKI,YAAA;CjDukKH;AiDnkKC;;;;EAEE,sBAAA;EACA,YAAA;EACA,0BAAA;CjDukKH;AiDnkKD;EACE,YAAA;EACA,iBAAA;CjDqkKD;AiDhkKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDkkKH;AiDvkKC;;;EASI,eAAA;CjDmkKL;AiD5kKC;;;EAYI,eAAA;CjDqkKL;AiDhkKC;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;CjDkkKH;AiDxkKC;;;;;;;;;EAYI,eAAA;CjDukKL;AiDnlKC;;;EAeI,eAAA;CjDykKL;AkD3qKC;EACE,eAAA;EACA,0BAAA;ClD6qKH;AkD3qKG;;EAEE,eAAA;ClD6qKL;AkD/qKG;;EAKI,eAAA;ClD8qKP;AkD3qKK;;;;EAEE,eAAA;EACA,0BAAA;ClD+qKP;AkD7qKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDkrKP;AkDxsKC;EACE,eAAA;EACA,0BAAA;ClD0sKH;AkDxsKG;;EAEE,eAAA;ClD0sKL;AkD5sKG;;EAKI,eAAA;ClD2sKP;AkDxsKK;;;;EAEE,eAAA;EACA,0BAAA;ClD4sKP;AkD1sKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD+sKP;AkDruKC;EACE,eAAA;EACA,0BAAA;ClDuuKH;AkDruKG;;EAEE,eAAA;ClDuuKL;AkDzuKG;;EAKI,eAAA;ClDwuKP;AkDruKK;;;;EAEE,eAAA;EACA,0BAAA;ClDyuKP;AkDvuKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD4uKP;AkDlwKC;EACE,eAAA;EACA,0BAAA;ClDowKH;AkDlwKG;;EAEE,eAAA;ClDowKL;AkDtwKG;;EAKI,eAAA;ClDqwKP;AkDlwKK;;;;EAEE,eAAA;EACA,0BAAA;ClDswKP;AkDpwKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDywKP;AiDxqKD;EACE,cAAA;EACA,mBAAA;CjD0qKD;AiDxqKD;EACE,iBAAA;EACA,iBAAA;CjD0qKD;AmDpyKD;EACE,oBAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL6uKT;AmDnyKD;EACE,cAAA;CnDqyKD;AmDhyKD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5BuzKF;AmDtyKD;EAMI,eAAA;CnDmyKH;AmD9xKD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnDgyKD;AmDpyKD;;;;;EAWI,eAAA;CnDgyKH;AmD3xKD;EACE,mBAAA;EACA,0BAAA;EACA,2BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bs0KF;AmDrxKD;;EAGI,iBAAA;CnDsxKH;AmDzxKD;;EAMM,oBAAA;EACA,iBAAA;CnDuxKL;AmDnxKG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B61KF;AmDjxKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5B21KF;AmD1yKD;EvB1DE,2BAAA;EACC,0BAAA;C5Bu2KF;AmD7wKD;EAEI,oBAAA;CnD8wKH;AmD3wKD;EACE,oBAAA;CnD6wKD;AmDrwKD;;;EAII,iBAAA;CnDswKH;AmD1wKD;;;EAOM,mBAAA;EACA,oBAAA;CnDwwKL;AmDhxKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B63KF;AmDrxKD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDwwKP;AmD5xKD;;;;;;;;EAwBU,4BAAA;CnD8wKT;AmDtyKD;;;;;;;;EA4BU,6BAAA;CnDoxKT;AmDhzKD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bq5KF;AmDrzKD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDkxKP;AmD5zKD;;;;;;;;EA8CU,+BAAA;CnDwxKT;AmDt0KD;;;;;;;;EAkDU,gCAAA;CnD8xKT;AmDh1KD;;;;EA2DI,2BAAA;CnD2xKH;AmDt1KD;;EA+DI,cAAA;CnD2xKH;AmD11KD;;EAmEI,UAAA;CnD2xKH;AmD91KD;;;;;;;;;;;;EA0EU,eAAA;CnDkyKT;AmD52KD;;;;;;;;;;;;EA8EU,gBAAA;CnD4yKT;AmD13KD;;;;;;;;EAuFU,iBAAA;CnD6yKT;AmDp4KD;;;;;;;;EAgGU,iBAAA;CnD8yKT;AmD94KD;EAsGI,UAAA;EACA,iBAAA;CnD2yKH;AmDjyKD;EACE,oBAAA;CnDmyKD;AmDpyKD;EAKI,iBAAA;EACA,mBAAA;CnDkyKH;AmDxyKD;EASM,gBAAA;CnDkyKL;AmD3yKD;EAcI,iBAAA;CnDgyKH;AmD9yKD;;EAkBM,2BAAA;CnDgyKL;AmDlzKD;EAuBI,cAAA;CnD8xKH;AmDrzKD;EAyBM,8BAAA;CnD+xKL;AmDxxKD;EC1PE,mBAAA;CpDqhLD;AoDnhLC;EACE,eAAA;EACA,0BAAA;EACA,mBAAA;CpDqhLH;AoDxhLC;EAMI,uBAAA;CpDqhLL;AoD3hLC;EASI,eAAA;EACA,0BAAA;CpDqhLL;AoDlhLC;EAEI,0BAAA;CpDmhLL;AmDvyKD;EC7PE,sBAAA;CpDuiLD;AoDriLC;EACE,YAAA;EACA,0BAAA;EACA,sBAAA;CpDuiLH;AoD1iLC;EAMI,0BAAA;CpDuiLL;AoD7iLC;EASI,eAAA;EACA,uBAAA;CpDuiLL;AoDpiLC;EAEI,6BAAA;CpDqiLL;AmDtzKD;EChQE,sBAAA;CpDyjLD;AoDvjLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDyjLH;AoD5jLC;EAMI,0BAAA;CpDyjLL;AoD/jLC;EASI,eAAA;EACA,0BAAA;CpDyjLL;AoDtjLC;EAEI,6BAAA;CpDujLL;AmDr0KD;ECnQE,sBAAA;CpD2kLD;AoDzkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2kLH;AoD9kLC;EAMI,0BAAA;CpD2kLL;AoDjlLC;EASI,eAAA;EACA,0BAAA;CpD2kLL;AoDxkLC;EAEI,6BAAA;CpDykLL;AmDp1KD;ECtQE,sBAAA;CpD6lLD;AoD3lLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6lLH;AoDhmLC;EAMI,0BAAA;CpD6lLL;AoDnmLC;EASI,eAAA;EACA,0BAAA;CpD6lLL;AoD1lLC;EAEI,6BAAA;CpD2lLL;AmDn2KD;ECzQE,sBAAA;CpD+mLD;AoD7mLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD+mLH;AoDlnLC;EAMI,0BAAA;CpD+mLL;AoDrnLC;EASI,eAAA;EACA,0BAAA;CpD+mLL;AoD5mLC;EAEI,6BAAA;CpD6mLL;AqD7nLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD+nLD;AqDpoLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD+nLH;AqD1nLD;EACE,uBAAA;CrD4nLD;AqDxnLD;EACE,oBAAA;CrD0nLD;AsDrpLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CLgmLT;AsD/pLD;EASI,mBAAA;EACA,kCAAA;CtDypLH;AsDppLD;EACE,cAAA;EACA,mBAAA;CtDspLD;AsDppLD;EACE,aAAA;EACA,mBAAA;CtDspLD;AuD5qLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,0BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBqrLD;AuD7qLC;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB6rLD;AuDzqLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvD2qLH;AwDhsLD;EACE,iBAAA;CxDksLD;AwD9rLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD6rLD;AwD1rLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL6gLT;AwDhsLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLwlLT;AwDpsLD;EACE,mBAAA;EACA,iBAAA;CxDssLD;AwDlsLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDosLD;AwDhsLD;EACE,mBAAA;EACA,uBAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDksLD;AwD9rLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,uBAAA;CxDgsLD;AwD9rLC;ElCrEA,WAAA;EAGA,yBAAA;CtBowLD;AwDjsLC;ElCtEA,aAAA;EAGA,0BAAA;CtBwwLD;AwDhsLD;EACE,cAAA;EACA,iCAAA;CxDksLD;AwD9rLD;EACE,iBAAA;CxDgsLD;AwD5rLD;EACE,UAAA;EACA,wBAAA;CxD8rLD;AwDzrLD;EACE,mBAAA;EACA,cAAA;CxD2rLD;AwDvrLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDyrLD;AwD5rLD;EAQI,iBAAA;EACA,iBAAA;CxDurLH;AwDhsLD;EAaI,kBAAA;CxDsrLH;AwDnsLD;EAiBI,eAAA;CxDqrLH;AwDhrLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDkrLD;AwDhqLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD+qLD;EwD7qLD;InDvEA,kDAAA;IACQ,0CAAA;GLuvLP;EwD5qLD;IAAY,aAAA;GxD+qLX;CACF;AwD1qLD;EAFE;IAAY,aAAA;GxDgrLX;CACF;AyD/zLD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBs1LD;AyD30LC;EnCdA,aAAA;EAGA,0BAAA;CtB01LD;AyD90LC;EAAW,iBAAA;EAAmB,eAAA;CzDk1L/B;AyDj1LC;EAAW,iBAAA;EAAmB,eAAA;CzDq1L/B;AyDp1LC;EAAW,gBAAA;EAAmB,eAAA;CzDw1L/B;AyDv1LC;EAAW,kBAAA;EAAmB,eAAA;CzD21L/B;AyDv1LD;EACE,iBAAA;EACA,iBAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;CzDy1LD;AyDr1LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDu1LD;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,yBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,wBAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;A2Dl7LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,uBAAA;EACA,qCAAA;UAAA,6BAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLk5LT;A2D77LC;EAAY,kBAAA;C3Dg8Lb;A2D/7LC;EAAY,kBAAA;C3Dk8Lb;A2Dj8LC;EAAY,iBAAA;C3Do8Lb;A2Dn8LC;EAAY,mBAAA;C3Ds8Lb;A2Dn8LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Dq8LD;A2Dl8LD;EACE,kBAAA;C3Do8LD;A2D57LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D87LH;A2D37LD;EACE,mBAAA;C3D67LD;A2D37LD;EACE,mBAAA;EACA,YAAA;C3D67LD;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,uBAAA;C3D47LL;A2Dz7LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;C3D47LL;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,0BAAA;C3D47LL;A2Dx7LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3D07LH;A2Dz7LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,wBAAA;EACA,cAAA;C3D27LL;A4DpjMD;EACE,mBAAA;C5DsjMD;A4DnjMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DqjMD;A4DxjMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLy4LT;A4D/jMD;;EAcM,eAAA;C5DqjML;A4D3hMC;EA4NF;IvD3DE,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL86LP;E4DzjMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D4jML;E4D1jMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D6jML;E4D3jMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D8jML;CACF;A4DpmMD;;;EA6CI,eAAA;C5D4jMH;A4DzmMD;EAiDI,QAAA;C5D2jMH;A4D5mMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5D0jMH;A4DlnMD;EA4DI,WAAA;C5DyjMH;A4DrnMD;EA+DI,YAAA;C5DyjMH;A4DxnMD;;EAmEI,QAAA;C5DyjMH;A4D5nMD;EAuEI,YAAA;C5DwjMH;A4D/nMD;EA0EI,WAAA;C5DwjMH;A4DhjMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,mCAAA;C5DmjMD;A4D9iMC;EdnGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CopMH;A4DljMC;EACE,WAAA;EACA,SAAA;EdxGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C6pMH;A4DpjMC;;EAEE,WAAA;EACA,YAAA;EACA,sBAAA;EtCvHF,aAAA;EAGA,0BAAA;CtB4qMD;A4DtlMD;;;;EAuCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DqjMH;A4DhmMD;;EA+CI,UAAA;EACA,mBAAA;C5DqjMH;A4DrmMD;;EAoDI,WAAA;EACA,oBAAA;C5DqjMH;A4D1mMD;;EAyDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DqjMH;A4DhjMG;EACE,iBAAA;C5DkjML;A4D9iMG;EACE,iBAAA;C5DgjML;A4DtiMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DwiMD;A4DjjMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D8hMH;A4D7jMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;C5D8hMH;A4DvhMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;C5DyhMD;A4DxhMC;EACE,kBAAA;C5D0hMH;A4Dj/LD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DmhMH;E4D3hMD;;IAYI,mBAAA;G5DmhMH;E4D/hMD;;IAgBI,oBAAA;G5DmhMH;E4D9gMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5DghMD;E4D5gMD;IACE,aAAA;G5D8gMD;CACF;A6D7wMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7D6yMH;A6D3yMC;;;;;;;;;;;;;;;;EACE,YAAA;C7D4zMH;AiCp0MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D+0MD;AiCt0MD;EACE,wBAAA;CjCw0MD;AiCt0MD;EACE,uBAAA;CjCw0MD;AiCh0MD;EACE,yBAAA;CjCk0MD;AiCh0MD;EACE,0BAAA;CjCk0MD;AiCh0MD;EACE,mBAAA;CjCk0MD;AiCh0MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/D41MD;AiC9zMD;EACE,yBAAA;CjCg0MD;AiCzzMD;EACE,gBAAA;CjC2zMD;AgE51MD;EACE,oBAAA;ChE81MD;AgEx1MD;;;;ECdE,yBAAA;CjE42MD;AgEv1MD;;;;;;;;;;;;EAYE,yBAAA;ChEy1MD;AgEl1MD;EA6IA;IC7LE,0BAAA;GjEs4MC;EiEr4MD;IAAU,0BAAA;GjEw4MT;EiEv4MD;IAAU,8BAAA;GjE04MT;EiEz4MD;;IACU,+BAAA;GjE44MT;CACF;AgE51MD;EAwIA;IA1II,0BAAA;GhEk2MD;CACF;AgE51MD;EAmIA;IArII,2BAAA;GhEk2MD;CACF;AgE51MD;EA8HA;IAhII,iCAAA;GhEk2MD;CACF;AgE31MD;EAwHA;IC7LE,0BAAA;GjEo6MC;EiEn6MD;IAAU,0BAAA;GjEs6MT;EiEr6MD;IAAU,8BAAA;GjEw6MT;EiEv6MD;;IACU,+BAAA;GjE06MT;CACF;AgEr2MD;EAmHA;IArHI,0BAAA;GhE22MD;CACF;AgEr2MD;EA8GA;IAhHI,2BAAA;GhE22MD;CACF;AgEr2MD;EAyGA;IA3GI,iCAAA;GhE22MD;CACF;AgEp2MD;EAmGA;IC7LE,0BAAA;GjEk8MC;EiEj8MD;IAAU,0BAAA;GjEo8MT;EiEn8MD;IAAU,8BAAA;GjEs8MT;EiEr8MD;;IACU,+BAAA;GjEw8MT;CACF;AgE92MD;EA8FA;IAhGI,0BAAA;GhEo3MD;CACF;AgE92MD;EAyFA;IA3FI,2BAAA;GhEo3MD;CACF;AgE92MD;EAoFA;IAtFI,iCAAA;GhEo3MD;CACF;AgE72MD;EA8EA;IC7LE,0BAAA;GjEg+MC;EiE/9MD;IAAU,0BAAA;GjEk+MT;EiEj+MD;IAAU,8BAAA;GjEo+MT;EiEn+MD;;IACU,+BAAA;GjEs+MT;CACF;AgEv3MD;EAyEA;IA3EI,0BAAA;GhE63MD;CACF;AgEv3MD;EAoEA;IAtEI,2BAAA;GhE63MD;CACF;AgEv3MD;EA+DA;IAjEI,iCAAA;GhE63MD;CACF;AgEt3MD;EAyDA;ICrLE,yBAAA;GjEs/MC;CACF;AgEt3MD;EAoDA;ICrLE,yBAAA;GjE2/MC;CACF;AgEt3MD;EA+CA;ICrLE,yBAAA;GjEggNC;CACF;AgEt3MD;EA0CA;ICrLE,yBAAA;GjEqgNC;CACF;AgEn3MD;ECnJE,yBAAA;CjEygND;AgEh3MD;EA4BA;IC7LE,0BAAA;GjEqhNC;EiEphND;IAAU,0BAAA;GjEuhNT;EiEthND;IAAU,8BAAA;GjEyhNT;EiExhND;;IACU,+BAAA;GjE2hNT;CACF;AgE93MD;EACE,yBAAA;ChEg4MD;AgE33MD;EAqBA;IAvBI,0BAAA;GhEi4MD;CACF;AgE/3MD;EACE,yBAAA;ChEi4MD;AgE53MD;EAcA;IAhBI,2BAAA;GhEk4MD;CACF;AgEh4MD;EACE,yBAAA;ChEk4MD;AgE73MD;EAOA;IATI,iCAAA;GhEm4MD;CACF;AgE53MD;EACA;ICrLE,yBAAA;GjEojNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n border: 0;\n background-color: transparent;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #fff;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #fff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #ccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #fff;\n border: 1px solid #ddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #fff;\n border-color: #ddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #fff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #fff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #fff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #fff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #fff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #fff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #fff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: floor((@gutter / 2));\n padding-right: ceil((@gutter / 2));\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n\n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on