first commit
This commit is contained in:
+299
@@ -0,0 +1,299 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Identity.MongoDbCore", "src\AspNetCore.Identity.MongoDbCore.csproj", "{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{059DB57E-6EC2-4F6D-BE0F-343B00976DA9}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7819C5A0-617F-488D-9791-2C4859B9BF19}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Identity.MongoDbCore.IntegrationTests", "test\AspNetCore.Identity.MongoDbCore.IntegrationTests\AspNetCore.Identity.MongoDbCore.IntegrationTests.csproj", "{FA1D3288-90E8-4831-B533-A2B3D4F034B1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{EE14D20F-C6E9-4A4C-A13E-C1A90BCA8763}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA1D3288-90E8-4831-B533-A2B3D4F034B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA1D3288-90E8-4831-B533-A2B3D4F034B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA1D3288-90E8-4831-B533-A2B3D4F034B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA1D3288-90E8-4831-B533-A2B3D4F034B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{C2EB6500-79A2-410E-8BF7-ACACE86C7A0E} = {059DB57E-6EC2-4F6D-BE0F-343B00976DA9}
|
||||
{FA1D3288-90E8-4831-B533-A2B3D4F034B1} = {7819C5A0-617F-488D-9791-2C4859B9BF19}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B6A5D462-4085-442D-9E31-DD2CAD280D3C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,115 @@
|
||||
# Microsoft.AspNetCore.Identity.MongoDbCore
|
||||
A MongoDb UserStore and RoleStore adapter for Microsoft.AspNetCore.Identity 2.0.
|
||||
Allows you to use MongoDb instead of SQL server with Microsoft.AspNetCore.Identity 2.0.
|
||||
Covered by 730+ integration tests and unit tests from the modified Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test test suite.
|
||||
|
||||
# Usage examples
|
||||
|
||||
Your user and role entities must inherit from MongoIdentityUser<Guid> and MongoIdentityRole<TKey> in a way similar to the IdentityUser<TKey> and the IdentityRole<TKey> in Microsoft.AspNetCore.Identity.
|
||||
|
||||
```csharp
|
||||
|
||||
public class ApplicationRole : MongoIdentityRole<Guid>
|
||||
{
|
||||
public ApplicationRole() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public ApplicationRole(string roleName) : base(roleName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationUser : MongoIdentityUser<Guid>
|
||||
{
|
||||
public ApplicationUser() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public ApplicationUser(string userName, string email) : base(userName, email)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets called by the runtime. Use this method to add services to the container.
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(_hostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{_hostingEnvironment.EnvironmentName}.json", optional: true);
|
||||
|
||||
|
||||
Configuration = builder.Build();
|
||||
|
||||
services.AddOptions();
|
||||
|
||||
// add a global config object
|
||||
services.AddSingleton(Configuration);
|
||||
var mongoSettings = Configuration.GetSection(nameof(MongoDbSettings));
|
||||
var settings = Configuration.GetSection(nameof(MongoDbSettings)).Get<MongoDbSettings>();
|
||||
var mongoDbIdentityConfiguration = new MongoDbIdentityConfiguration
|
||||
{
|
||||
MongoDbSettings = settings,
|
||||
IdentityOptionsAction = options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequiredLength = 8;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
|
||||
// Lockout settings
|
||||
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
|
||||
options.Lockout.MaxFailedAccessAttempts = 10;
|
||||
|
||||
|
||||
// ApplicationUser settings
|
||||
options.User.RequireUniqueEmail = true;
|
||||
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@.-_";
|
||||
}
|
||||
};
|
||||
services.ConfigureMongoDbIdentity<ApplicationUser, ApplicationRole, Guid>(mongoDbIdentityConfiguration);
|
||||
}
|
||||
```
|
||||
|
||||
## Author
|
||||
**Alexandre Spieser**
|
||||
|
||||
## Donations
|
||||
Feeling like my work is worth a coffee?
|
||||
Donations are welcome and will go towards further development of this project as well as other MongoDb related projects. Use the wallet address below to donate.
|
||||
BTC Donations: 1Qc5ZpNA7g66KEEMcz7MXxwNyyoRyKJJZ
|
||||
|
||||
*Thank you for your support and generosity!*
|
||||
|
||||
## License
|
||||
mongodb-generic-repository is under MIT license - http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2017 Alexandre Spieser
|
||||
|
||||
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.
|
||||
|
||||
## Copyright
|
||||
Copyright © 2017
|
||||
@@ -0,0 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>bin\Release\netcoreapp2.0\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\netcoreapp2.0\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />-->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="MongoDbGenericRepository" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources.Designer.cs">
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>AspNetCore.Identity.MongoDbCore</id>
|
||||
<version>1.0.3</version>
|
||||
<title>AspNetCore.Identity.MongoDbCore</title>
|
||||
<authors>Alexandre Spieser</authors>
|
||||
<owners>Alexandre Spieser</owners>
|
||||
<licenseUrl>http://www.opensource.org/licenses/mit-license.php</licenseUrl>
|
||||
<projectUrl>https://github.com/alexandre-spieser/AspNetCore.Identity.MongoDbCore</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>A MongoDb UserStore and RoleStore adapter for Microsoft.AspNetCore.Identity 2.0.</description>
|
||||
<releaseNotes>First release.</releaseNotes>
|
||||
<copyright>Copyright 2017 (c) Alexandre Spieser. All rights reserved.</copyright>
|
||||
<tags>aspnetcore mongo mongodb identity membership</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="lib\**" target="lib" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,122 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Interfaces;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The extensions for an object that holds claims.
|
||||
/// </summary>
|
||||
public static class ClaimHolderExtensions
|
||||
{
|
||||
public static MongoClaim ToMongoClaim(this Claim claim)
|
||||
{
|
||||
return new MongoClaim
|
||||
{
|
||||
Type = claim.Type,
|
||||
Value = claim.Value,
|
||||
Issuer = claim.Issuer
|
||||
};
|
||||
}
|
||||
|
||||
public static Claim ToClaim(this MongoClaim claim)
|
||||
{
|
||||
return new Claim(claim.Type, claim.Value, null, claim.Issuer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a claim to a claim holder, implementing <see cref="IClaimHolder"/>.
|
||||
/// </summary>
|
||||
/// <param name="claimHolder">The object holding claims.</param>
|
||||
/// <param name="claim">The claim you want to add.</param>
|
||||
/// <returns>Returns true if the claim was added.</returns>
|
||||
public static bool AddClaim(this IClaimHolder claimHolder, Claim claim)
|
||||
{
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
|
||||
// prevent adding duplicate claims
|
||||
if (claimHolder.HasClaim(claim))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
claimHolder.Claims.Add(claim.ToMongoClaim());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a claim on a claim holder, implementing <see cref="IClaimHolder"/>.
|
||||
/// </summary>
|
||||
/// <param name="claimHolder">The object holding claims.</param>
|
||||
/// <param name="claim">The claim you want to replace.</param>
|
||||
/// <param name="newClaim">The new claim you want to set.</param>
|
||||
/// <returns>Returns true if the claim was replaced.</returns>
|
||||
public static bool ReplaceClaim(this IClaimHolder claimHolder, Claim claim, Claim newClaim)
|
||||
{
|
||||
var replaced = false;
|
||||
claimHolder.Claims.Where(uc => uc.Value == claim.Value && uc.Type == claim.Type).ToList()
|
||||
.ForEach(oldClaim => {
|
||||
oldClaim.Type = newClaim.Type;
|
||||
oldClaim.Value = newClaim.Value;
|
||||
oldClaim.Issuer = newClaim.Issuer;
|
||||
replaced |= true;
|
||||
});
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an object implementing <see cref="IClaimHolder"/> has a claim.
|
||||
/// </summary>
|
||||
/// <param name="claimHolder">The object holding claims.</param>
|
||||
/// <param name="claim">The claim you want to replace.</param>
|
||||
/// <returns>Returns true if the claim is present, false otherwise.</returns>
|
||||
public static bool HasClaim(this IClaimHolder claimHolder, Claim claim)
|
||||
{
|
||||
if(claimHolder.Claims == null)
|
||||
{
|
||||
claimHolder.Claims = new List<MongoClaim>();
|
||||
}
|
||||
return claimHolder.Claims.Any(e => e.Value == claim.Value && e.Type == claim.Type);
|
||||
}
|
||||
|
||||
public static bool RemoveClaim(this IClaimHolder claimHolder, Claim claim)
|
||||
{
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
var exists = claimHolder.Claims
|
||||
.FirstOrDefault(e => e.Value == claim.Value
|
||||
&& e.Type == claim.Type);
|
||||
if (exists != null)
|
||||
{
|
||||
claimHolder.Claims.Remove(exists);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool RemoveClaims(this IClaimHolder claimHolder, IEnumerable<Claim> claims)
|
||||
{
|
||||
var someClaimsRemoved = false;
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
var matchedClaims = claimHolder.Claims.Where(uc => uc.Value == claim.Value && uc.Type == claim.Type)
|
||||
.ToList();
|
||||
|
||||
foreach (var c in matchedClaims)
|
||||
{
|
||||
claimHolder.Claims.Remove(c);
|
||||
someClaimsRemoved |= true;
|
||||
}
|
||||
}
|
||||
return someClaimsRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MongoDbGenericRepository;
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Extensions
|
||||
{
|
||||
public class MongoDbSettings
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
}
|
||||
|
||||
public class MongoDbIdentityConfiguration
|
||||
{
|
||||
public MongoDbSettings MongoDbSettings { get; set; }
|
||||
public Action<IdentityOptions> IdentityOptionsAction { get; set; }
|
||||
}
|
||||
|
||||
public static class ServiceCollectionExtension
|
||||
{
|
||||
public static void ConfigureMongoDbIdentity<TUser, TKey>(
|
||||
this IServiceCollection services,
|
||||
MongoDbIdentityConfiguration mongoDbIdentityConfiguration,
|
||||
IMongoRepository mongoRepository = null)
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
services.AddSingleton<MongoDbSettings>(mongoDbIdentityConfiguration.MongoDbSettings);
|
||||
services.AddSingleton<IMongoRepository>(provider =>
|
||||
{
|
||||
var options = provider.GetService<MongoDbSettings>();
|
||||
return mongoRepository ?? new MongoRepository(options.ConnectionString, options.DatabaseName);
|
||||
});
|
||||
|
||||
CommonMongoDbSetup<TUser, MongoIdentityRole<TKey>, TKey>(services, mongoDbIdentityConfiguration);
|
||||
}
|
||||
|
||||
public static void ConfigureMongoDbIdentity<TUser>(this IServiceCollection services, MongoDbIdentityConfiguration mongoDbIdentityConfiguration)
|
||||
where TUser : MongoIdentityUser, new()
|
||||
{
|
||||
services.AddSingleton<MongoDbSettings>(mongoDbIdentityConfiguration.MongoDbSettings);
|
||||
services.AddSingleton<IMongoRepository>(provider =>
|
||||
{
|
||||
var options = provider.GetService<MongoDbSettings>();
|
||||
return new MongoRepository(options.ConnectionString, options.DatabaseName);
|
||||
});
|
||||
|
||||
CommonMongoDbSetup<TUser, MongoIdentityRole, Guid>(services, mongoDbIdentityConfiguration);
|
||||
}
|
||||
|
||||
|
||||
public static void ConfigureMongoDbIdentity<TUser, TRole, TKey>(this IServiceCollection services, MongoDbIdentityConfiguration mongoDbIdentityConfiguration,
|
||||
IMongoDbContext mongoDbContext = null)
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TRole : MongoIdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
services.AddSingleton<MongoDbSettings>(mongoDbIdentityConfiguration.MongoDbSettings);
|
||||
services.AddSingleton<IMongoRepository>(provider =>
|
||||
{
|
||||
var options = provider.GetService<MongoDbSettings>();
|
||||
return mongoDbContext == null ? new MongoRepository(options.ConnectionString, options.DatabaseName) : new MongoRepository(mongoDbContext);
|
||||
});
|
||||
|
||||
CommonMongoDbSetup<TUser, TRole, TKey>(services, mongoDbIdentityConfiguration);
|
||||
}
|
||||
|
||||
private static void CommonMongoDbSetup<TUser, TRole, TKey>(this IServiceCollection services, MongoDbIdentityConfiguration mongoDbIdentityConfiguration)
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TRole : MongoIdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
services.AddScoped<IUserStore<TUser>>(provider =>
|
||||
{
|
||||
var userStore = new MongoUserStore<TUser, TRole, IMongoDbContext, TKey>(provider.GetService<IMongoRepository>().Context);
|
||||
return userStore;
|
||||
});
|
||||
|
||||
services.AddScoped<IRoleStore<TRole>>(provider =>
|
||||
{
|
||||
return new MongoRoleStore<TRole, IMongoDbContext, TKey>(provider.GetService<IMongoRepository>().Context);
|
||||
});
|
||||
|
||||
services.AddIdentity<TUser, TRole>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.Configure<IdentityOptions>(mongoDbIdentityConfiguration.IdentityOptionsAction);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore
|
||||
{
|
||||
public static class GlobalVariables
|
||||
{
|
||||
public static Random Random = new Random();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using MongoDbGenericRepository;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Infrastructure
|
||||
{
|
||||
public interface IMongoRepository : IBaseMongoRepository
|
||||
{
|
||||
void DropCollection<TDocument>();
|
||||
void DropCollection<TDocument>(string partitionKey);
|
||||
IMongoDbContext Context { get; }
|
||||
}
|
||||
|
||||
|
||||
public class MongoRepository : BaseMongoRepository, IMongoRepository
|
||||
{
|
||||
public MongoRepository(string connectionString, string databaseName) : base(connectionString, databaseName)
|
||||
{
|
||||
}
|
||||
|
||||
public MongoRepository(IMongoDbContext mongoDbContext) : base(mongoDbContext)
|
||||
{
|
||||
}
|
||||
|
||||
public void DropCollection<TDocument>()
|
||||
{
|
||||
MongoDbContext.DropCollection<TDocument>();
|
||||
}
|
||||
|
||||
public void DropCollection<TDocument>(string partitionKey)
|
||||
{
|
||||
MongoDbContext.DropCollection<TDocument>(partitionKey);
|
||||
}
|
||||
|
||||
public IMongoDbContext Context => MongoDbContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface for an object that holds claims.
|
||||
/// </summary>
|
||||
public interface IClaimHolder
|
||||
{
|
||||
List<MongoClaim> Claims { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MongoDbGenericRepository.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Models
|
||||
{
|
||||
public class MongoDbIdentityRole : MongoIdentityRole<string>
|
||||
{
|
||||
public MongoDbIdentityRole() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public MongoDbIdentityRole(string roleName) : base(roleName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityRole : MongoIdentityRole<Guid>
|
||||
{
|
||||
public MongoIdentityRole() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public MongoIdentityRole(string roleName) : base(roleName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityRole<TKey> : IdentityRole<TKey>, IDocument<TKey>, IClaimHolder
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
private void InitializeFields()
|
||||
{
|
||||
Version = 1;
|
||||
Claims = new List<MongoClaim>();
|
||||
Guid guidValue = Guid.NewGuid();
|
||||
var idTypeName = typeof(TKey).Name;
|
||||
switch (idTypeName)
|
||||
{
|
||||
case "Guid":
|
||||
Id = (TKey)(object)guidValue;
|
||||
break;
|
||||
case "Int32":
|
||||
Id = (TKey)(object)GlobalVariables.Random.Next(1, int.MaxValue);
|
||||
break;
|
||||
case "String":
|
||||
Id = (TKey)(object)guidValue.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public MongoIdentityRole()
|
||||
{
|
||||
InitializeFields();
|
||||
}
|
||||
|
||||
public MongoIdentityRole(string roleName)
|
||||
{
|
||||
Name = roleName;
|
||||
InitializeFields();
|
||||
}
|
||||
|
||||
public MongoIdentityRole(string name, TKey key)
|
||||
{
|
||||
InitializeFields();
|
||||
Id = key;
|
||||
Name = Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The version of the role schema
|
||||
/// </summary>
|
||||
public int Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The claims associated to the role
|
||||
/// </summary>
|
||||
public List<MongoClaim> Claims { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MongoDbGenericRepository.Models;
|
||||
using System.Linq;
|
||||
using MongoDB.Driver;
|
||||
using AspNetCore.Identity.MongoDbCore.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Models
|
||||
{
|
||||
public class Token
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the LoginProvider this token is from.
|
||||
/// </summary>
|
||||
public string LoginProvider { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the token.
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the token value.
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class MongoClaim {
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
public string Issuer { get; set; }
|
||||
}
|
||||
|
||||
public class UserRole
|
||||
{
|
||||
public object UserId { get; set; }
|
||||
public object RoleId { get; set; }
|
||||
}
|
||||
|
||||
public class MongoDbIdentityUser : MongoIdentityUser<string>
|
||||
{
|
||||
public MongoDbIdentityUser() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public MongoDbIdentityUser(string userName) : base(userName)
|
||||
{
|
||||
}
|
||||
|
||||
public MongoDbIdentityUser(string userName, string email) : base(userName, email)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityUser : MongoIdentityUser<Guid>
|
||||
{
|
||||
public MongoIdentityUser() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public MongoIdentityUser(string userName) : base(userName)
|
||||
{
|
||||
}
|
||||
|
||||
public MongoIdentityUser(string userName, string email) : base(userName, email)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityUser<TKey> : IdentityUser<TKey>, IDocument<TKey>, IClaimHolder
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
public int Version { get; set; }
|
||||
|
||||
public DateTime CreatedOn { get; private set; }
|
||||
public DateTime? LockoutEndDate { get; private set; }
|
||||
public DateTime? DeletedOn { get; private set; }
|
||||
public List<MongoClaim> Claims { get; set; }
|
||||
public List<TKey> Roles { get; set; }
|
||||
public List<UserLoginInfo> Logins { get; set; }
|
||||
public List<Token> Tokens { get; set; }
|
||||
|
||||
private void InitializeFields()
|
||||
{
|
||||
Claims = new List<MongoClaim>();
|
||||
Logins = new List<UserLoginInfo>();
|
||||
Roles = new List<TKey>();
|
||||
Tokens = new List<Token>();
|
||||
Guid guidValue = Guid.NewGuid();
|
||||
|
||||
var idTypeName = typeof(TKey).Name;
|
||||
switch (idTypeName)
|
||||
{
|
||||
case "Guid":
|
||||
Id = (TKey)(object)guidValue;
|
||||
break;
|
||||
case "Int32":
|
||||
Id = (TKey)(object)GlobalVariables.Random.Next(1, int.MaxValue);
|
||||
break;
|
||||
case "String":
|
||||
Id = (TKey)(object)guidValue.ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public MongoIdentityUser()
|
||||
{
|
||||
CreatedOn = DateTime.UtcNow;
|
||||
SetVersion(1);
|
||||
InitializeFields();
|
||||
}
|
||||
|
||||
public MongoIdentityUser(string userName, string email) : this(userName)
|
||||
{
|
||||
if (email != null)
|
||||
{
|
||||
Email = email.ToLowerInvariant().Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public MongoIdentityUser(string userName)
|
||||
{
|
||||
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
|
||||
CreatedOn = DateTime.UtcNow;
|
||||
|
||||
SetVersion(1);
|
||||
InitializeFields();
|
||||
Roles = new List<TKey>();
|
||||
}
|
||||
|
||||
public virtual MongoIdentityUser<TKey> SetId(TKey key)
|
||||
{
|
||||
Id = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual MongoIdentityUser<TKey> SetVersion(int version)
|
||||
{
|
||||
Version = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual void EnableTwoFactorAuthentication()
|
||||
{
|
||||
TwoFactorEnabled = true;
|
||||
}
|
||||
|
||||
public virtual void DisableTwoFactorAuthentication()
|
||||
{
|
||||
TwoFactorEnabled = false;
|
||||
}
|
||||
|
||||
public virtual void EnableLockout()
|
||||
{
|
||||
LockoutEnabled = true;
|
||||
}
|
||||
|
||||
public virtual void DisableLockout()
|
||||
{
|
||||
LockoutEnabled = false;
|
||||
}
|
||||
|
||||
public virtual void SetEmail(string email)
|
||||
{
|
||||
Email = email ?? throw new ArgumentNullException(nameof(email));
|
||||
}
|
||||
|
||||
public virtual void SetNormalizedUserName(string normalizedUserName)
|
||||
{
|
||||
NormalizedUserName = normalizedUserName ?? throw new ArgumentNullException(nameof(normalizedUserName));
|
||||
}
|
||||
|
||||
public virtual void SetPhoneNumber(string phoneNumber)
|
||||
{
|
||||
PhoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public virtual void SetPasswordHash(string passwordHash)
|
||||
{
|
||||
PasswordHash = passwordHash;
|
||||
}
|
||||
|
||||
public virtual void SetSecurityStamp(string securityStamp)
|
||||
{
|
||||
SecurityStamp = securityStamp;
|
||||
}
|
||||
|
||||
public virtual void SetAccessFailedCount(int accessFailedCount)
|
||||
{
|
||||
AccessFailedCount = accessFailedCount;
|
||||
}
|
||||
|
||||
public virtual void ResetAccessFailedCount()
|
||||
{
|
||||
AccessFailedCount = 0;
|
||||
}
|
||||
|
||||
public virtual void LockUntil(DateTime lockoutEndDate)
|
||||
{
|
||||
LockoutEndDate = lockoutEndDate;
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (DeletedOn != null)
|
||||
{
|
||||
throw new InvalidOperationException($"User '{Id}' has already been deleted.");
|
||||
}
|
||||
|
||||
DeletedOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
#region Role Management
|
||||
|
||||
public virtual IdentityUserRole<TKey> GetUserRole(TKey roleId)
|
||||
{
|
||||
var foundRoleId = Roles.FirstOrDefault(e => e.Equals(roleId));
|
||||
if (!foundRoleId.Equals(default(TKey)))
|
||||
{
|
||||
return new IdentityUserRole<TKey>
|
||||
{
|
||||
UserId = Id,
|
||||
RoleId = foundRoleId
|
||||
};
|
||||
}
|
||||
return default(IdentityUserRole<TKey>);
|
||||
}
|
||||
|
||||
public virtual bool RemoveRole(TKey roleId)
|
||||
{
|
||||
var roleClaim = Roles.FirstOrDefault(e => e.Equals(roleId));
|
||||
if (!roleClaim.Equals(default(TKey)))
|
||||
{
|
||||
Roles.Remove(roleId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool AddRole(TKey roleId)
|
||||
{
|
||||
if (!Roles.Contains(roleId))
|
||||
{
|
||||
Roles.Add(roleId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Login Management
|
||||
|
||||
public virtual bool AddLogin(UserLoginInfo mongoUserLogin)
|
||||
{
|
||||
if (mongoUserLogin == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mongoUserLogin));
|
||||
}
|
||||
if (HasLogin(mongoUserLogin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Logins.Add(mongoUserLogin);
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool HasLogin(UserLoginInfo login)
|
||||
{
|
||||
return Logins.Any(e => e.LoginProvider == login.LoginProvider && e.ProviderKey == e.ProviderKey);
|
||||
}
|
||||
|
||||
public virtual void RemoveLogin(UserLoginInfo mongoUserLogin)
|
||||
{
|
||||
if (mongoUserLogin == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mongoUserLogin));
|
||||
}
|
||||
|
||||
Logins.Remove(mongoUserLogin);
|
||||
}
|
||||
|
||||
public virtual IdentityUserLogin<TKey> GetUserLogin(string loginProvider, string providerKey)
|
||||
{
|
||||
|
||||
var login = Logins.FirstOrDefault(e => e.LoginProvider == loginProvider && e.ProviderKey == providerKey);
|
||||
if (login != null)
|
||||
{
|
||||
return new IdentityUserLogin<TKey>
|
||||
{
|
||||
UserId = Id,
|
||||
LoginProvider = login.LoginProvider,
|
||||
ProviderDisplayName = login.ProviderDisplayName,
|
||||
ProviderKey = login.ProviderKey
|
||||
};
|
||||
}
|
||||
return default(IdentityUserLogin<TKey>);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Token Management
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a claim on a claim holder, implementing <see cref="IClaimHolder"/>.
|
||||
/// </summary>
|
||||
/// <param name="claimHolder">The object holding claims.</param>
|
||||
/// <param name="claim">The claim you want to replace.</param>
|
||||
/// <param name="newClaim">The new claim you want to set.</param>
|
||||
/// <returns>Returns true if the claim was replaced.</returns>
|
||||
public bool SetToken(IdentityUserToken<TKey> tokenToset, string value)
|
||||
{
|
||||
var token = Tokens.FirstOrDefault(e => e.LoginProvider == tokenToset.LoginProvider && e.Name == tokenToset.Name);
|
||||
if (token != null)
|
||||
{
|
||||
token.Value = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IdentityUserToken<TKey> GetToken(string loginProvider, string name)
|
||||
{
|
||||
var token = Tokens.FirstOrDefault(e => e.LoginProvider == loginProvider && e.Name == name);
|
||||
if (token != null)
|
||||
{
|
||||
return new IdentityUserToken<TKey>
|
||||
{
|
||||
UserId = Id,
|
||||
LoginProvider = token.LoginProvider,
|
||||
Name = token.Name,
|
||||
Value = token.Value
|
||||
};
|
||||
}
|
||||
return default(IdentityUserToken<TKey>);
|
||||
}
|
||||
|
||||
public bool HasToken(IdentityUserToken<TKey> token)
|
||||
{
|
||||
return Tokens.Any(e => e.LoginProvider == token.LoginProvider
|
||||
&& e.Name == token.Name
|
||||
&& e.Value == token.Value);
|
||||
}
|
||||
|
||||
public bool AddOrSet(IdentityUserToken<TKey> token)
|
||||
{
|
||||
var exists = GetToken(token.LoginProvider, token.Name);
|
||||
if (exists != null && exists.Value != token.Value)
|
||||
{
|
||||
return SetToken(exists, token.Value);
|
||||
}
|
||||
if (exists == null)
|
||||
{
|
||||
Tokens.Add(new Token
|
||||
{
|
||||
LoginProvider = token.LoginProvider,
|
||||
Name = token.Name,
|
||||
Value = token.Value
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AddUserToken<TUserToken>(TUserToken token) where TUserToken : IdentityUserToken<TKey>
|
||||
{
|
||||
if (HasToken(token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Tokens.Add(new Token
|
||||
{
|
||||
LoginProvider = token.LoginProvider,
|
||||
Name = token.Name,
|
||||
Value = token.Value
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveUserToken<TUserToken>(TUserToken token) where TUserToken : IdentityUserToken<TKey>
|
||||
{
|
||||
var exists = Tokens.FirstOrDefault(e => e.LoginProvider == token.LoginProvider && e.Name == token.Name);
|
||||
if (exists == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Tokens.Remove(exists);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Token Management
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using MongoDB.Bson.Serialization.Conventions;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore
|
||||
{
|
||||
internal static class MongoConfig
|
||||
{
|
||||
private static bool _initialized = false;
|
||||
private static object _initializationLock = new object();
|
||||
private static object _initializationTarget;
|
||||
|
||||
public static void EnsureConfigured()
|
||||
{
|
||||
EnsureConfiguredImpl();
|
||||
}
|
||||
|
||||
private static void EnsureConfiguredImpl()
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(ref _initializationTarget, ref _initialized, ref _initializationLock, () =>
|
||||
{
|
||||
Configure();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static void Configure()
|
||||
{
|
||||
RegisterConventions();
|
||||
}
|
||||
|
||||
private static void RegisterConventions()
|
||||
{
|
||||
var pack = new ConventionPack
|
||||
{
|
||||
new IgnoreIfNullConvention(false),
|
||||
new CamelCaseElementNameConvention(),
|
||||
};
|
||||
|
||||
ConventionRegistry.Register("AspNetCore.Identity.MongoDB", pack, IsConventionApplicable);
|
||||
}
|
||||
|
||||
private static bool IsConventionApplicable(Type type)
|
||||
{
|
||||
return type == typeof(MongoIdentityUser<>);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using MongoDB.Driver;
|
||||
using MongoDbGenericRepository;
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore
|
||||
{
|
||||
|
||||
public class MongoIdentityDbContext : MongoDbContext
|
||||
{
|
||||
|
||||
public MongoIdentityDbContext(string connectionString, string databaseName) : base(connectionString, databaseName)
|
||||
{
|
||||
}
|
||||
|
||||
public IMongoRepository MongoRepository { get; }
|
||||
|
||||
public IMongoCollection<MongoIdentityUser> Users
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<MongoIdentityUser>();
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<MongoIdentityRole> Roles
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<MongoIdentityRole>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityDbContext<TUser> : MongoDbContext
|
||||
where TUser : MongoIdentityUser
|
||||
{
|
||||
|
||||
public MongoIdentityDbContext(string connectionString, string databaseName) : base(connectionString, databaseName)
|
||||
{
|
||||
}
|
||||
|
||||
public IMongoRepository MongoRepository { get; }
|
||||
|
||||
public IMongoCollection<TUser> Users
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<TUser>();
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<MongoIdentityRole> Roles
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<MongoIdentityRole>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoIdentityDbContext<TUser, TRole, TKey> : MongoDbContext
|
||||
where TUser : MongoIdentityUser<TKey>
|
||||
where TRole : MongoIdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
public MongoIdentityDbContext(string connectionString, string databaseName) : base(connectionString, databaseName)
|
||||
{
|
||||
}
|
||||
|
||||
public IMongoCollection<TUser> Users
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<TUser>();
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<TRole> Roles
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetCollection<TRole>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the password hashing options
|
||||
/// </summary>
|
||||
public sealed class PasswordHasherOptionsAccessor : IOptions<PasswordHasherOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets options which use the IdentityV3 compat mode, and set the iteration count to 200000 PBKDF2-SHA256 iterations
|
||||
/// (roughly 200ms of work)
|
||||
/// </summary>
|
||||
public PasswordHasherOptions Value { get; } = new PasswordHasherOptions
|
||||
{
|
||||
CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3,
|
||||
IterationCount = 200000
|
||||
};
|
||||
}
|
||||
|
||||
public static class MongoIdentityServiceCollectionExtensions
|
||||
{
|
||||
public static IdentityBuilder AddIdentity<TUser>(this IServiceCollection services)
|
||||
where TUser : class => services.AddIdentity<TUser>(null);
|
||||
|
||||
public static IdentityBuilder AddIdentity<TUser>(this IServiceCollection services, Action<IdentityOptions> setupAction)
|
||||
where TUser : class
|
||||
{
|
||||
// Hosting doesn't add IHttpContextAccessor by default
|
||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// Identity services
|
||||
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
|
||||
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
||||
|
||||
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
||||
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
|
||||
|
||||
// No interface for the error describer so we can add errors without rev'ing the interface
|
||||
services.TryAddScoped<IdentityErrorDescriber>();
|
||||
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
|
||||
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser>>();
|
||||
services.TryAddScoped<UserManager<TUser>, AspNetUserManager<TUser>>();
|
||||
services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
|
||||
|
||||
if (setupAction != null)
|
||||
{
|
||||
services.Configure(setupAction);
|
||||
}
|
||||
|
||||
return new IdentityBuilder(typeof(TUser), services);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
// 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 MongoDbGenericRepository;
|
||||
using MongoDB.Driver;
|
||||
using AspNetCore.Identity.MongoDbCore.Extensions;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role</typeparam>
|
||||
public class MongoRoleStore<TRole> : MongoRoleStore<TRole, MongoDbContext, string>
|
||||
where TRole : MongoIdentityRole<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MongoRoleStore{TRole}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public MongoRoleStore(IMongoDbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
public class MongoRoleStore<TRole, TContext> : MongoRoleStore<TRole, TContext, string>
|
||||
where TRole : MongoIdentityRole<string>
|
||||
where TContext : IMongoDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MongoRoleStore{TRole, TContext}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public MongoRoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
public class MongoRoleStore<TRole, TContext, TKey> : MongoRoleStore<TRole, TContext, TKey, IdentityUserRole<TKey>, IdentityRoleClaim<TKey>>,
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : MongoIdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : IMongoDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MongoRoleStore{TRole, TContext, TKey}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public MongoRoleStore(IMongoDbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type of the class representing a user role.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type of the class representing a role claim.</typeparam>
|
||||
public class MongoRoleStore<TRole, TContext, TKey, TUserRole, TRoleClaim> :
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : MongoIdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : IMongoDbContext
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="MongoRoleStore{TRole, TContext, TKey, TUserRole, TRoleClaim}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public MongoRoleStore(IMongoDbContext context, IdentityErrorDescriber describer = null)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
Context = context;
|
||||
ErrorDescriber = describer ?? new IdentityErrorDescriber();
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
private static IMongoDbContext Context { get; set; }
|
||||
|
||||
private static IMongoRepository _mongoRepository;
|
||||
private static IMongoRepository MongoRepository
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mongoRepository == null)
|
||||
{
|
||||
_mongoRepository = new MongoRepository(Context);
|
||||
}
|
||||
return _mongoRepository;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
||||
/// </summary>
|
||||
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to create in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
await MongoRepository.AddOneAsync<TRole, TKey>(role);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to update in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
var oldStamp = role.ConcurrencyStamp;
|
||||
role.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
var collection = MongoRepository.Context.GetCollection<TRole>();
|
||||
var updateRes = await collection.ReplaceOneAsync(x => x.Id.Equals(role.Id)
|
||||
&& x.ConcurrencyStamp.Equals(oldStamp),
|
||||
role);
|
||||
if (updateRes.ModifiedCount == 0)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to delete from the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
var oldStamp = role.ConcurrencyStamp;
|
||||
role.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
var collection = MongoRepository.Context.GetCollection<TRole>();
|
||||
var deleteRes = await collection.DeleteOneAsync(x => x.Id.Equals(role.Id)
|
||||
&& x.ConcurrencyStamp.Equals(oldStamp));
|
||||
if (deleteRes.DeletedCount == 0)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose ID should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the ID of the role.</returns>
|
||||
public virtual Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(ConvertIdToString(role.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of a role in the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be set.</param>
|
||||
/// <param name="roleName">The name of the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (role.Name != roleName)
|
||||
{
|
||||
role.Name = roleName;
|
||||
return MongoRepository.UpdateOneAsync<TRole, TKey, string>(role, x => x.Name, role.Name);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to a strongly typed key object.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided <paramref name="id"/>.</returns>
|
||||
public virtual TKey ConvertIdFromString(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return default(TKey);
|
||||
}
|
||||
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
|
||||
public virtual string ConvertIdToString(TKey id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.Equals(default(TKey)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return id.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified ID as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="id">The role ID to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public virtual Task<TRole> FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var roleId = ConvertIdFromString(id);
|
||||
return MongoRepository.GetOneAsync<TRole, TKey>(u => u.Id.Equals(roleId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="normalizedName">The normalized role name to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public virtual Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return MongoRepository.GetOneAsync<TRole, TKey>(r => r.NormalizedName == normalizedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.NormalizedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be set.</param>
|
||||
/// <param name="normalizedName">The normalized name to set</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (role.NormalizedName != normalizedName)
|
||||
{
|
||||
role.NormalizedName = normalizedName;
|
||||
return MongoRepository.UpdateOneAsync<TRole, TKey, string>(role, x => x.NormalizedName, role.NormalizedName);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws if this class has been disposed.
|
||||
/// </summary>
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stores
|
||||
/// </summary>
|
||||
public void Dispose() => _disposed = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
|
||||
public async virtual Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return role.Claims.Select(e => e.ToClaim()).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the claim to.</param>
|
||||
/// <param name="claim">The claim to add to the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
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));
|
||||
}
|
||||
if (role.AddClaim(claim))
|
||||
{
|
||||
MongoRepository.UpdateOne<TRole, TKey, List<MongoClaim>>(role, e => e.Claims, role.Claims);
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the claim from.</param>
|
||||
/// <param name="claim">The claim to remove from the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
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));
|
||||
}
|
||||
if (role.RemoveClaim(claim))
|
||||
{
|
||||
await MongoRepository.UpdateOneAsync<TRole, TKey, List<MongoClaim>>(role, e => e.Claims, role.Claims);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the roles the store contains.
|
||||
/// </summary>
|
||||
public virtual IQueryable<TRole> Roles => Context.GetCollection<TRole>().AsQueryable();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a entity representing a role claim.
|
||||
/// </summary>
|
||||
/// <param name="role">The associated role.</param>
|
||||
/// <param name="claim">The associated claim.</param>
|
||||
/// <returns>The role claim entity.</returns>
|
||||
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
|
||||
=> new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Generated
+99
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Ce code a été généré par un outil.
|
||||
// Version du runtime :4.0.30319.42000
|
||||
//
|
||||
// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
|
||||
// le code est régénéré.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées.
|
||||
/// </summary>
|
||||
// Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder
|
||||
// à l'aide d'un outil, tel que ResGen ou Visual Studio.
|
||||
// Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen
|
||||
// avec l'option /str ou régénérez votre projet VS.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.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() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retourne l'instance ResourceManager mise en cache utilisée par cette classe.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AspNetCore.Identity.MongoDbCore.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remplace la propriété CurrentUICulture du thread actuel pour toutes
|
||||
/// les recherches de ressources à l'aide de cette classe de ressource fortement typée.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey, TUserRole, TRoleClaim>..
|
||||
/// </summary>
|
||||
internal static string NotIdentityRole {
|
||||
get {
|
||||
return ResourceManager.GetString("NotIdentityRole", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>..
|
||||
/// </summary>
|
||||
internal static string NotIdentityUser {
|
||||
get {
|
||||
return ResourceManager.GetString("NotIdentityUser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à Role {0} does not exist..
|
||||
/// </summary>
|
||||
internal static string RoleNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("RoleNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à Value cannot be null or empty..
|
||||
/// </summary>
|
||||
internal static string ValueCannotBeNullOrEmpty {
|
||||
get {
|
||||
return ResourceManager.GetString("ValueCannotBeNullOrEmpty", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NotIdentityRole" xml:space="preserve">
|
||||
<value>AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey, TUserRole, TRoleClaim>.</value>
|
||||
<comment>error when the role does not derive from IdentityRole</comment>
|
||||
</data>
|
||||
<data name="NotIdentityUser" xml:space="preserve">
|
||||
<value>AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>.</value>
|
||||
<comment>error when the user does not derive from IdentityUser</comment>
|
||||
</data>
|
||||
<data name="RoleNotFound" xml:space="preserve">
|
||||
<value>Role {0} does not exist.</value>
|
||||
<comment>error when a role does not exist</comment>
|
||||
</data>
|
||||
<data name="ValueCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Value cannot be null or empty.</value>
|
||||
<comment>error when something cannot be null or empty</comment>
|
||||
</data>
|
||||
</root>
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NotIdentityRole" xml:space="preserve">
|
||||
<value>AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey, TUserRole, TRoleClaim>.</value>
|
||||
<comment>error when the role does not derive from IdentityRole</comment>
|
||||
</data>
|
||||
<data name="NotIdentityUser" xml:space="preserve">
|
||||
<value>AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>.</value>
|
||||
<comment>error when the user does not derive from IdentityUser</comment>
|
||||
</data>
|
||||
<data name="RoleNotFound" xml:space="preserve">
|
||||
<value>Role {0} does not exist.</value>
|
||||
<comment>error when a role does not exist</comment>
|
||||
</data>
|
||||
<data name="ValueCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Value cannot be null or empty.</value>
|
||||
<comment>error when something cannot be null or empty</comment>
|
||||
</data>
|
||||
</root>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCore.Identity.MongoDbCore" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="MongoDbGenericRepository" Version="1.3.0" />
|
||||
<PackageReference Include="Moq" Version="4.7.142" />
|
||||
<PackageReference Include="xunit" Version="2.3.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+14
@@ -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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class ApiConsistencyTest : ApiConsistencyTestBase
|
||||
{
|
||||
protected override Assembly TargetAssembly => typeof(IdentityUser).GetTypeInfo().Assembly;
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
// 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 Xunit;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
//public class CustomPocoTest : IClassFixture<MongoDatabaseFixture>
|
||||
//{
|
||||
// private readonly MongoDatabaseFixture _fixture;
|
||||
|
||||
// public CustomPocoTest(MongoDatabaseFixture fixture)
|
||||
// {
|
||||
// _fixture = fixture;
|
||||
// }
|
||||
|
||||
// public class User<TKey> where TKey : IEquatable<TKey>
|
||||
// {
|
||||
// public TKey Id { get; set; }
|
||||
// public string UserName { get; set; }
|
||||
// }
|
||||
|
||||
// public class CustomDbContext<TKey> : DbContext where TKey : IEquatable<TKey>
|
||||
// {
|
||||
// public CustomDbContext(DbContextOptions options) : base(options)
|
||||
// { }
|
||||
|
||||
// public DbSet<User<TKey>> Users { get; set; }
|
||||
|
||||
// }
|
||||
|
||||
// public CustomDbContext<TKey> GetContext<TKey>() where TKey : IEquatable<TKey>
|
||||
// {
|
||||
// return DbUtil.Create<CustomDbContext<TKey>>(_fixture.ConnectionString);
|
||||
// }
|
||||
|
||||
// public CustomDbContext<TKey> CreateContext<TKey>(bool delete = false) where TKey : IEquatable<TKey>
|
||||
// {
|
||||
// var db = GetContext<TKey>();
|
||||
// if (delete)
|
||||
// {
|
||||
// db.Database.EnsureDeleted();
|
||||
// }
|
||||
// db.Database.EnsureCreated();
|
||||
// return db;
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanUpdateNameGuid()
|
||||
// {
|
||||
// using (var db = CreateContext<Guid>(true))
|
||||
// {
|
||||
// var oldName = Guid.NewGuid().ToString();
|
||||
// var user = new User<Guid> { UserName = oldName, Id = Guid.NewGuid() };
|
||||
// db.Users.Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// var newName = Guid.NewGuid().ToString();
|
||||
// user.UserName = newName;
|
||||
// await db.SaveChangesAsync();
|
||||
// Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName));
|
||||
// Assert.Equal(user, db.Users.Single(u => u.UserName == newName));
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanUpdateNameString()
|
||||
// {
|
||||
// using (var db = CreateContext<string>(true))
|
||||
// {
|
||||
// var oldName = Guid.NewGuid().ToString();
|
||||
// var user = new User<string> { UserName = oldName, Id = Guid.NewGuid().ToString() };
|
||||
// db.Users.Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// var newName = Guid.NewGuid().ToString();
|
||||
// user.UserName = newName;
|
||||
// await db.SaveChangesAsync();
|
||||
// Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName));
|
||||
// Assert.Equal(user, db.Users.Single(u => u.UserName == newName));
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanCreateUserInt()
|
||||
// {
|
||||
// using (var db = CreateContext<int>(true))
|
||||
// {
|
||||
// var user = new User<int>();
|
||||
// db.Users.Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// user.UserName = "Boo";
|
||||
// await db.SaveChangesAsync();
|
||||
// var fetch = db.Users.First(u => u.UserName == "Boo");
|
||||
// Assert.Equal(user, fetch);
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanCreateUserIntViaSet()
|
||||
// {
|
||||
// using (var db = CreateContext<int>(true))
|
||||
// {
|
||||
// var user = new User<int>();
|
||||
// var users = db.Set<User<int>>();
|
||||
// users.Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// user.UserName = "Boo";
|
||||
// await db.SaveChangesAsync();
|
||||
// var fetch = users.First(u => u.UserName == "Boo");
|
||||
// Assert.Equal(user, fetch);
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanUpdateNameInt()
|
||||
// {
|
||||
// using (var db = CreateContext<int>(true))
|
||||
// {
|
||||
// var oldName = Guid.NewGuid().ToString();
|
||||
// var user = new User<int> { UserName = oldName };
|
||||
// db.Users.Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// var newName = Guid.NewGuid().ToString();
|
||||
// user.UserName = newName;
|
||||
// await db.SaveChangesAsync();
|
||||
// Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName));
|
||||
// Assert.Equal(user, db.Users.Single(u => u.UserName == newName));
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task CanUpdateNameIntWithSet()
|
||||
// {
|
||||
// using (var db = CreateContext<int>(true))
|
||||
// {
|
||||
// var oldName = Guid.NewGuid().ToString();
|
||||
// var user = new User<int> { UserName = oldName };
|
||||
// db.Set<User<int>>().Add(user);
|
||||
// await db.SaveChangesAsync();
|
||||
// var newName = Guid.NewGuid().ToString();
|
||||
// user.UserName = newName;
|
||||
// await db.SaveChangesAsync();
|
||||
// Assert.Null(db.Set<User<int>>().SingleOrDefault(u => u.UserName == oldName));
|
||||
// Assert.Equal(user, db.Set<User<int>>().Single(u => u.UserName == newName));
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
// 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.Linq.Expressions;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using MongoDbGenericRepository;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public abstract class SqlStoreOnlyUsersTestBase<TUser, TKey> : UserManagerSpecificationTestBase<TUser, TKey>, IClassFixture<MongoDatabaseFixture<TUser, TKey>>
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
private readonly MongoDatabaseFixture<TUser, TKey> _fixture;
|
||||
|
||||
protected SqlStoreOnlyUsersTestBase(MongoDatabaseFixture<TUser, TKey> fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipDbTests()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
var user = new TUser
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
_fixture.UsersToDelete.Add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
protected override Expression<Func<TUser, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<TUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
public IMongoDbContext CreateContext()
|
||||
{
|
||||
return Container.MongoRepository.Context;
|
||||
}
|
||||
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<TUser>>(new MongoUserOnlyStore<TUser, IMongoDbContext, TKey>(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(TUser user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteUserRemovesTokensTest()
|
||||
{
|
||||
// Need fail if not empty?
|
||||
var userMgr = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await userMgr.SetAuthenticationTokenAsync(user, "provider", "test", "value"));
|
||||
|
||||
Assert.Equal("value", await userMgr.GetAuthenticationTokenAsync(user, "provider", "test"));
|
||||
|
||||
IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user));
|
||||
|
||||
Assert.Null(await userMgr.GetAuthenticationTokenAsync(user, "provider", "test"));
|
||||
}
|
||||
|
||||
private IQueryable<TUser> GetQueryable()
|
||||
{
|
||||
return Container.MongoRepository.Context.GetCollection<TUser>().AsQueryable();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCreateUserUsingEF()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
Container.MongoRepository.AddOne<TUser, TKey>(user);
|
||||
Assert.True(GetQueryable().Any(u => u.UserName == user.UserName));
|
||||
Assert.NotNull(GetQueryable().FirstOrDefault(u => u.UserName == user.UserName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateUsingManager()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
private async Task LazyLoadTestSetup(IMongoDbContext db, TUser user)
|
||||
{
|
||||
var context = CreateContext();
|
||||
var manager = CreateManager(context);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo("provider", user.Id.ToString(), "display")));
|
||||
Claim[] userClaims =
|
||||
{
|
||||
new Claim("Whatever", "Value"),
|
||||
new Claim("Whatever2", "Value2")
|
||||
};
|
||||
foreach (var c in userClaims)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByIdTest()
|
||||
{
|
||||
var db = CreateContext();
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(db, user);
|
||||
|
||||
db = CreateContext();
|
||||
var manager = CreateManager(db);
|
||||
|
||||
var userById = await manager.FindByIdAsync(user.Id.ToString());
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userById)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userById)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userById)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByNameTest()
|
||||
{
|
||||
var db = CreateContext();
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(db, user);
|
||||
|
||||
db = CreateContext();
|
||||
var manager = CreateManager(db);
|
||||
var userByName = await manager.FindByNameAsync(user.UserName);
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByName)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByName)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByName)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByLoginTest()
|
||||
{
|
||||
var db = CreateContext();
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(db, user);
|
||||
|
||||
db = CreateContext();
|
||||
var manager = CreateManager(db);
|
||||
var userByLogin = await manager.FindByLoginAsync("provider", user.Id.ToString());
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByLogin)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByLogin)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByLogin)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByEmailTest()
|
||||
{
|
||||
var db = CreateContext();
|
||||
var user = CreateTestUser();
|
||||
user.Email = "fooz@fizzy.pop";
|
||||
await LazyLoadTestSetup(db, user);
|
||||
|
||||
db = CreateContext();
|
||||
var manager = CreateManager(db);
|
||||
var userByEmail = await manager.FindByEmailAsync(user.Email);
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByEmail)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByEmail)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByEmail)).Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
// 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.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore.Extensions;
|
||||
using AspNetCore.Identity.MongoDbCore;
|
||||
using MongoDB.Driver;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using MongoDbGenericRepository;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
// TODO: Add test variation with non IdentityDbContext
|
||||
|
||||
public abstract class SqlStoreTestBase<TUser, TRole, TKey> : IdentitySpecificationTestBase<TUser, TRole, TKey>,
|
||||
IClassFixture<MongoDatabaseFixture<TUser, TRole, TKey>>
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TRole : MongoIdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
private readonly MongoDatabaseFixture<TUser, TRole, TKey> _fixture;
|
||||
|
||||
protected SqlStoreTestBase(MongoDatabaseFixture<TUser, TRole, TKey> fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
protected override void SetupIdentityServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
// configure the default type name
|
||||
services.ConfigureMongoDbIdentity<TUser, TRole, TKey>(Container.MongoDbIdentityConfiguration, Container.MongoRepository.Context);
|
||||
|
||||
services.AddLogging();
|
||||
services.AddSingleton<ILogger<UserManager<TUser>>>(new TestLogger<UserManager<TUser>>());
|
||||
services.AddSingleton<ILogger<RoleManager<TRole>>>(new TestLogger<RoleManager<TRole>>());
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipDbTests()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override TUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
var user = new TUser
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
_fixture.UsersToDelete.Add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
protected override TRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
|
||||
{
|
||||
var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
|
||||
var role = new TRole() { Name = roleName };
|
||||
_fixture.RolesToDelete.Add(role);
|
||||
return role;
|
||||
}
|
||||
|
||||
protected override Expression<Func<TRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
|
||||
|
||||
protected override Expression<Func<TUser, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<TRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
|
||||
|
||||
protected override Expression<Func<TUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<TUser>>(new MongoUserStore<TUser, TRole, IMongoDbContext, TKey>(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<TRole>>(new MongoRoleStore<TRole, IMongoDbContext, TKey>(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(TUser user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteRoleNonEmptySucceedsTest()
|
||||
{
|
||||
var userMgr = CreateManager();
|
||||
var roleMgr = CreateRoleManager();
|
||||
var roleName = "delete" + Guid.NewGuid().ToString();
|
||||
var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true);
|
||||
Assert.False(await roleMgr.RoleExistsAsync(roleName));
|
||||
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, roleName));
|
||||
var roles = await userMgr.GetRolesAsync(user);
|
||||
Assert.Single(roles);
|
||||
IdentityResultAssert.IsSuccess(await roleMgr.DeleteAsync(role));
|
||||
Assert.Null(await roleMgr.FindByNameAsync(roleName));
|
||||
Assert.False(await roleMgr.RoleExistsAsync(roleName));
|
||||
// REVIEW: We should throw if deleteing a non empty role?
|
||||
roles = await userMgr.GetRolesAsync(user);
|
||||
|
||||
Assert.Empty(roles);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteUserRemovesFromRoleTest()
|
||||
{
|
||||
// Need fail if not empty?
|
||||
var userMgr = CreateManager();
|
||||
var roleMgr = CreateRoleManager();
|
||||
var roleName = "deleteUserRemove" + Guid.NewGuid().ToString();
|
||||
var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true);
|
||||
Assert.False(await roleMgr.RoleExistsAsync(roleName));
|
||||
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, roleName));
|
||||
|
||||
var roles = await userMgr.GetRolesAsync(user);
|
||||
Assert.Single(roles);
|
||||
|
||||
IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user));
|
||||
|
||||
roles = await userMgr.GetRolesAsync(user);
|
||||
Assert.Empty(roles);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteUserRemovesTokensTest()
|
||||
{
|
||||
// Need fail if not empty?
|
||||
var userMgr = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await userMgr.SetAuthenticationTokenAsync(user, "provider", "test", "value"));
|
||||
|
||||
Assert.Equal("value", await userMgr.GetAuthenticationTokenAsync(user, "provider", "test"));
|
||||
|
||||
IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user));
|
||||
|
||||
Assert.Null(await userMgr.GetAuthenticationTokenAsync(user, "provider", "test"));
|
||||
}
|
||||
|
||||
private IQueryable<TUser> GetQueryable()
|
||||
{
|
||||
return Container.MongoRepository.Context.GetCollection<TUser>().AsQueryable();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCreateUserUsingEF()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
Container.MongoRepository.AddOne<TUser, TKey>(user);
|
||||
Assert.True(GetQueryable().Any(u => u.UserName == user.UserName));
|
||||
Assert.NotNull(GetQueryable().FirstOrDefault(u => u.UserName == user.UserName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateUsingManager()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
private async Task LazyLoadTestSetup(TUser user)
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var role = CreateRoleManager();
|
||||
var admin = CreateTestRole("Admin" + Guid.NewGuid().ToString());
|
||||
var local = CreateTestRole("Local" + Guid.NewGuid().ToString());
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo("provider", user.Id.ToString(), "display")));
|
||||
IdentityResultAssert.IsSuccess(await role.CreateAsync(admin));
|
||||
IdentityResultAssert.IsSuccess(await role.CreateAsync(local));
|
||||
IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, admin.Name));
|
||||
IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, local.Name));
|
||||
Claim[] userClaims =
|
||||
{
|
||||
new Claim("Whatever", "Value"),
|
||||
new Claim("Whatever2", "Value2")
|
||||
};
|
||||
foreach (var c in userClaims)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByIdTest()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(user);
|
||||
|
||||
var manager = CreateManager();
|
||||
|
||||
var userById = await manager.FindByIdAsync(user.Id.ToString());
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userById)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userById)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userById)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByNameTest()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(user);
|
||||
var manager = CreateManager();
|
||||
var userByName = await manager.FindByNameAsync(user.UserName);
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByName)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByName)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByName)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByLoginTest()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
await LazyLoadTestSetup(user);
|
||||
|
||||
var manager = CreateManager();
|
||||
var userByLogin = await manager.FindByLoginAsync("provider", user.Id.ToString());
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByLogin)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByLogin)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByLogin)).Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadFromDbFindByEmailTest()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
user.Email = "fooz@fizzy.pop";
|
||||
await LazyLoadTestSetup(user);
|
||||
|
||||
var manager = CreateManager();
|
||||
var userByEmail = await manager.FindByEmailAsync(user.Email);
|
||||
Assert.Equal(2, (await manager.GetClaimsAsync(userByEmail)).Count);
|
||||
Assert.Equal(1, (await manager.GetLoginsAsync(userByEmail)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByEmail)).Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// 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.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore;
|
||||
using MongoDbGenericRepository;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class GuidUser : MongoIdentityUser<Guid>
|
||||
{
|
||||
public GuidUser() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class GuidRole : MongoIdentityRole<Guid>
|
||||
{
|
||||
public GuidRole() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreGuidTest : SqlStoreTestBase<GuidUser, GuidRole, Guid>
|
||||
{
|
||||
public UserStoreGuidTest(MongoDatabaseFixture<GuidUser, GuidRole, Guid> fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
public class ApplicationUserStore : MongoUserStore<GuidUser, GuidRole, IMongoDbContext, Guid>
|
||||
{
|
||||
public ApplicationUserStore(IMongoDbContext context) : base(Container.MongoRepository.Context) { }
|
||||
}
|
||||
|
||||
public class ApplicationRoleStore : MongoRoleStore<GuidRole, IMongoDbContext, Guid>
|
||||
{
|
||||
public ApplicationRoleStore(IMongoDbContext context) : base(Container.MongoRepository.Context) { }
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<GuidUser>>(new ApplicationUserStore(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<GuidRole>>(new ApplicationRoleStore(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+33
@@ -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;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class IntUser : MongoIdentityUser<int>
|
||||
{
|
||||
public IntUser() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class IntRole : MongoIdentityRole<int>
|
||||
{
|
||||
public IntRole() : base()
|
||||
{
|
||||
Name = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreIntTest : SqlStoreTestBase<IntUser, IntRole, int>
|
||||
{
|
||||
public UserStoreIntTest(MongoDatabaseFixture<IntUser, IntRole, int> fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -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;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class StringUser : MongoDbIdentityUser
|
||||
{
|
||||
public StringUser() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class StringRole : MongoDbIdentityRole
|
||||
{
|
||||
public StringRole() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreStringKeyTest : SqlStoreTestBase<StringUser, StringRole, string>
|
||||
{
|
||||
public UserStoreStringKeyTest(MongoDatabaseFixture<StringUser, StringRole, string> fixture)
|
||||
: base(fixture)
|
||||
{ }
|
||||
|
||||
}
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
// 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.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore;
|
||||
using MongoDB.Driver;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using MongoDbGenericRepository;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
|
||||
public class ApplicationDbContext : MongoIdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(string connectionString, string databaseName) : base(connectionString, databaseName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Applicationcontext
|
||||
{
|
||||
public static ApplicationDbContext Instance = new ApplicationDbContext(
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.ConnectionString,
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.DatabaseName);
|
||||
}
|
||||
|
||||
public class UserStoreTest : IdentitySpecificationTestBase<MongoDbIdentityUser, MongoDbIdentityRole>, IClassFixture<MongoDatabaseFixture<MongoDbIdentityUser, MongoDbIdentityRole, string>>
|
||||
{
|
||||
private readonly MongoDatabaseFixture<MongoDbIdentityUser, MongoDbIdentityRole,string> _fixture;
|
||||
|
||||
public UserStoreTest(MongoDatabaseFixture<MongoDbIdentityUser, MongoDbIdentityRole,string> fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipDbTests()
|
||||
=> false;
|
||||
|
||||
[Fact]
|
||||
public void CanCreateUserUsingEF()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
user.Id = Guid.NewGuid().ToString();
|
||||
var guidString = user.Id.ToString();
|
||||
user.UserName = guidString;
|
||||
Container.MongoRepository.AddOne<MongoDbIdentityUser, string>(user);
|
||||
Assert.True(Container.MongoRepository.Any<MongoDbIdentityUser, string>(u => u.UserName == guidString));
|
||||
Assert.NotNull(Container.MongoRepository.GetOne<MongoDbIdentityUser, string>(u => u.UserName == guidString));
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<MongoDbIdentityUser>>(new MongoUserStore<MongoDbIdentityUser, MongoDbIdentityRole, IMongoDbContext, string>(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<MongoDbIdentityRole>>(new MongoRoleStore<MongoDbIdentityRole, IMongoDbContext>(Container.MongoRepository.Context));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SqlUserStoreMethodsThrowWhenDisposedTest()
|
||||
{
|
||||
var store = new MongoUserStore(Container.MongoRepository.Context);
|
||||
store.Dispose();
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddLoginAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddToRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetClaimsAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetLoginsAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetRolesAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.IsInRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveLoginAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.RemoveFromRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.ReplaceClaimAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByLoginAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByIdAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByNameAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.DeleteAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.SetEmailConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetEmailConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.SetPhoneNumberConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.GetPhoneNumberConfirmedAsync(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UserStorePublicNullCheckTest()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>("context", () => new MongoUserStore(null));
|
||||
var store = new MongoUserStore(Container.MongoRepository.Context);
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserIdAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserNameAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetUserNameAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.DeleteAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.ReplaceClaimAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetClaimsAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLoginsAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetRolesAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddLoginAsync(null, null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.RemoveLoginAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddToRoleAsync(null, null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.RemoveFromRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.IsInRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetPasswordHashAsync(null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetPasswordHashAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetSecurityStampAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetSecurityStampAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("login", async () => await store.AddLoginAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("claims",
|
||||
async () => await store.AddClaimsAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("claims",
|
||||
async () => await store.RemoveClaimsAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetEmailConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetEmailConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetEmailAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetEmailAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetPhoneNumberAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetPhoneNumberAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.GetPhoneNumberConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetPhoneNumberConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetTwoFactorEnabledAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetTwoFactorEnabledAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLockoutEnabledAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetLockoutEnabledAsync(null, false));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLockoutEndDateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetLockoutEndDateAsync(null, new DateTimeOffset()));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.ResetAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.IncrementAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.AddToRoleAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.RemoveFromRoleAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.IsInRoleAsync(new MongoDbIdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.AddToRoleAsync(new MongoDbIdentityUser("fake"), ""));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.RemoveFromRoleAsync(new MongoDbIdentityUser("fake"), ""));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.IsInRoleAsync(new MongoDbIdentityUser("fake"), ""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateUsingManager()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TwoUsersSamePasswordDifferentHash()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var userA = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(userA, "password"));
|
||||
var userB = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(userB, "password"));
|
||||
|
||||
Assert.NotEqual(userA.PasswordHash, userB.PasswordHash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddUserToUnknownRoleFails()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var u = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(u));
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await manager.AddToRoleAsync(u, "bogus"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentUpdatesWillFail()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
var manager = CreateManager();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var manager1 = CreateManager();
|
||||
var manager2 = CreateManager();
|
||||
var user1 = await manager1.FindByIdAsync(user.Id);
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user1);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user1, user2);
|
||||
user1.UserName = Guid.NewGuid().ToString();
|
||||
user2.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentUpdatesWillFailWithDetachedUser()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
var manager = CreateManager();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var manager1 = CreateManager();
|
||||
var manager2 = CreateManager();
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user, user2);
|
||||
user.UserName = Guid.NewGuid().ToString();
|
||||
user2.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAModifiedUserWillFail()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
|
||||
var manager = CreateManager();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
||||
var manager1 = CreateManager();
|
||||
var manager2 = CreateManager();
|
||||
var user1 = await manager1.FindByIdAsync(user.Id);
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user1);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user1, user2);
|
||||
user1.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
|
||||
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentRoleUpdatesWillFail()
|
||||
{
|
||||
var role = CreateRole();
|
||||
|
||||
var manager = CreateRoleManager();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
|
||||
|
||||
var manager1 = CreateRoleManager();
|
||||
var manager2 = CreateRoleManager();
|
||||
var role1 = await manager1.FindByIdAsync(role.Id);
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role1);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role1, role2);
|
||||
role1.Name = Guid.NewGuid().ToString();
|
||||
role2.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentRoleUpdatesWillFailWithDetachedRole()
|
||||
{
|
||||
var role = CreateRole();
|
||||
var manager = CreateRoleManager();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
|
||||
var manager1 = CreateRoleManager();
|
||||
var manager2 = CreateRoleManager();
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role, role2);
|
||||
role.Name = Guid.NewGuid().ToString();
|
||||
role2.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
private MongoDbIdentityRole CreateRole()
|
||||
{
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
var role = new MongoDbIdentityRole(guid);
|
||||
_fixture.RolesToDelete.Add(role);
|
||||
return role;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAModifiedRoleWillFail()
|
||||
{
|
||||
var role = CreateRole();
|
||||
var manager = CreateRoleManager();
|
||||
var result = await manager.CreateAsync(role);
|
||||
|
||||
IdentityResultAssert.IsSuccess(result);
|
||||
|
||||
var manager1 = CreateRoleManager();
|
||||
var manager2 = CreateRoleManager(true);
|
||||
var role1 = await manager1.FindByIdAsync(role.Id);
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role1);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role1, role2);
|
||||
role1.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
|
||||
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
|
||||
protected override MongoDbIdentityUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
var user = new MongoDbIdentityUser
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
_fixture.UsersToDelete.Add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
protected override MongoDbIdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
|
||||
{
|
||||
var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
|
||||
var role = new MongoDbIdentityRole(roleName);
|
||||
_fixture.RolesToDelete.Add(role);
|
||||
return role;
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(MongoDbIdentityUser user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
protected override Expression<Func<MongoDbIdentityUser, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<MongoDbIdentityRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
|
||||
|
||||
protected override Expression<Func<MongoDbIdentityRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
|
||||
|
||||
protected override Expression<Func<MongoDbIdentityUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
}
|
||||
|
||||
public class ApplicationUser : MongoIdentityUser { }
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
// 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.Identity.Test;
|
||||
//using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using MongoDbGenericRepository;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class UserStoreWithGenericsTest : IdentitySpecificationTestBase<IdentityUserWithGenerics, MyIdentityRole, string>, IClassFixture<MongoDatabaseFixture<IdentityUserWithGenerics, MyIdentityRole, string>>
|
||||
{
|
||||
private readonly MongoDatabaseFixture<IdentityUserWithGenerics, MyIdentityRole, string> _fixture;
|
||||
|
||||
public UserStoreWithGenericsTest(MongoDatabaseFixture<IdentityUserWithGenerics, MyIdentityRole, string> fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipDbTests()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<IdentityUserWithGenerics>>(new MongoUserStore<IdentityUserWithGenerics>(Container.MongoRepository.Context));
|
||||
//services.AddSingleton<IUserStore<IdentityUserWithGenerics>>(new UserStoreWithGenerics((ContextWithGenerics)context, "TestContext"));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<MyIdentityRole>>(new MongoRoleStore<MyIdentityRole>(Container.MongoRepository.Context));
|
||||
//services.AddSingleton<IRoleStore<MyIdentityRole>>(new RoleStoreWithGenerics(Container.MongoRepository.Context, "TestContext"));
|
||||
}
|
||||
|
||||
protected override IdentityUserWithGenerics CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
var user = new IdentityUserWithGenerics
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
_fixture.UsersToDelete.Add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
protected override MyIdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
|
||||
{
|
||||
var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
|
||||
var role = new MyIdentityRole(roleName);
|
||||
_fixture.RolesToDelete.Add(role);
|
||||
return role;
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(IdentityUserWithGenerics user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
protected override Expression<Func<IdentityUserWithGenerics, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<MyIdentityRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
|
||||
|
||||
protected override Expression<Func<IdentityUserWithGenerics, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
protected override Expression<Func<MyIdentityRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresWithInvalidUserThrows()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddIdentity<object, IdentityRole>();
|
||||
var e = Assert.Throws<InvalidOperationException>(() =>
|
||||
//builder.AddEntityFrameworkStores<ContextWithGenerics>()
|
||||
Throw()
|
||||
);
|
||||
Assert.Contains("AddEntityFrameworkStores", e.Message);
|
||||
}
|
||||
|
||||
private void Throw()
|
||||
{
|
||||
throw new InvalidOperationException("AddEntityFrameworkStores");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresWithInvalidRoleThrows()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddIdentity<IdentityUser, object>();
|
||||
var e = Assert.Throws<InvalidOperationException>(() => {
|
||||
//builder.AddEntityFrameworkStores<ContextWithGenerics>();
|
||||
Throw();
|
||||
});
|
||||
Assert.Contains("AddEntityFrameworkStores", e.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanAddRemoveUserClaimWithIssuer()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
Claim[] claims = { new Claim("c1", "v1", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
|
||||
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);
|
||||
Assert.Equal(3, userClaims.Intersect(claims, ClaimEqualityComparer.Default).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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveClaimWithIssuerOnlyAffectsUser()
|
||||
{
|
||||
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", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
|
||||
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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReplaceUserClaimWithIssuer()
|
||||
{
|
||||
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", "i")));
|
||||
var userClaims = await manager.GetClaimsAsync(user);
|
||||
Assert.Equal(1, userClaims.Count);
|
||||
Claim claim = new Claim("c", "b", "i");
|
||||
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);
|
||||
Assert.Equal(claim.Issuer, newClaim.Issuer);
|
||||
}
|
||||
}
|
||||
|
||||
public class ClaimEqualityComparer : IEqualityComparer<Claim>
|
||||
{
|
||||
public static IEqualityComparer<Claim> Default = new ClaimEqualityComparer();
|
||||
|
||||
public bool Equals(Claim x, Claim y)
|
||||
{
|
||||
return x.Value == y.Value && x.Type == y.Type && x.Issuer == y.Issuer;
|
||||
}
|
||||
|
||||
public int GetHashCode(Claim obj)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Generic Type defintions
|
||||
|
||||
public class IdentityUserWithGenerics : MongoDbIdentityUser
|
||||
{
|
||||
public IdentityUserWithGenerics() : base()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreWithGenerics : MongoUserStore<IdentityUserWithGenerics, MyIdentityRole, IMongoDbContext, string, IdentityUserClaimWithIssuer, IdentityUserRoleWithDate, IdentityUserLoginWithContext, IdentityUserTokenWithStuff, IdentityRoleClaimWithIssuer>
|
||||
{
|
||||
public string LoginContext { get; set; }
|
||||
|
||||
public UserStoreWithGenerics(IMongoDbContext context, string loginContext) : base(context)
|
||||
{
|
||||
LoginContext = loginContext;
|
||||
}
|
||||
|
||||
protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
|
||||
{
|
||||
return new IdentityUserRoleWithDate()
|
||||
{
|
||||
RoleId = role.Id,
|
||||
UserId = user.Id,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim)
|
||||
{
|
||||
return new IdentityUserClaimWithIssuer { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer };
|
||||
}
|
||||
|
||||
protected override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)
|
||||
{
|
||||
return new IdentityUserLoginWithContext
|
||||
{
|
||||
UserId = user.Id,
|
||||
ProviderKey = login.ProviderKey,
|
||||
LoginProvider = login.LoginProvider,
|
||||
ProviderDisplayName = login.ProviderDisplayName,
|
||||
Context = LoginContext
|
||||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
|
||||
{
|
||||
return new IdentityUserTokenWithStuff
|
||||
{
|
||||
UserId = user.Id,
|
||||
LoginProvider = loginProvider,
|
||||
Name = name,
|
||||
Value = value,
|
||||
Stuff = "stuff"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class RoleStoreWithGenerics : MongoRoleStore<MyIdentityRole, IMongoDbContext, string, IdentityUserRoleWithDate, IdentityRoleClaimWithIssuer>
|
||||
{
|
||||
private string _loginContext;
|
||||
public RoleStoreWithGenerics(IMongoDbContext context, string loginContext) : base(context)
|
||||
{
|
||||
_loginContext = loginContext;
|
||||
}
|
||||
|
||||
protected override IdentityRoleClaimWithIssuer CreateRoleClaim(MyIdentityRole role, Claim claim)
|
||||
{
|
||||
return new IdentityRoleClaimWithIssuer { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer };
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentityUserClaimWithIssuer : IdentityUserClaim<string>
|
||||
{
|
||||
public string Issuer { get; set; }
|
||||
|
||||
public override Claim ToClaim()
|
||||
{
|
||||
return new Claim(ClaimType, ClaimValue, null, Issuer);
|
||||
}
|
||||
|
||||
public override void InitializeFromClaim(Claim other)
|
||||
{
|
||||
ClaimValue = other.Value;
|
||||
ClaimType = other.Type;
|
||||
Issuer = other.Issuer;
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentityRoleClaimWithIssuer : IdentityRoleClaim<string>
|
||||
{
|
||||
public string Issuer { get; set; }
|
||||
|
||||
public override Claim ToClaim()
|
||||
{
|
||||
return new Claim(ClaimType, ClaimValue, null, Issuer);
|
||||
}
|
||||
|
||||
public override void InitializeFromClaim(Claim other)
|
||||
{
|
||||
ClaimValue = other.Value;
|
||||
ClaimType = other.Type;
|
||||
Issuer = other.Issuer;
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentityUserRoleWithDate : IdentityUserRole<string>
|
||||
{
|
||||
public DateTime Created { get; set; }
|
||||
}
|
||||
|
||||
public class MyIdentityRole : MongoDbIdentityRole
|
||||
{
|
||||
public MyIdentityRole() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public MyIdentityRole(string roleName) : base(roleName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentityUserTokenWithStuff : IdentityUserToken<string>
|
||||
{
|
||||
public string Stuff { get; set; }
|
||||
}
|
||||
|
||||
public class IdentityUserLoginWithContext : IdentityUserLogin<string>
|
||||
{
|
||||
public string Context { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+69
@@ -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 MongoDbGenericRepository;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using MongoDB.Driver;
|
||||
using MongoDbGenericRepository.Models;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test
|
||||
{
|
||||
public class MongoDatabaseFixture<TUser, TKey> : IDisposable
|
||||
where TUser : IDocument<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
public IMongoDbContext Context;
|
||||
|
||||
public MongoDatabaseFixture()
|
||||
{
|
||||
Context = new MongoDbContext(
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.ConnectionString,
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.DatabaseName);
|
||||
UsersToDelete = new ConcurrentBag<TUser>();
|
||||
}
|
||||
public ConcurrentBag<TUser> UsersToDelete { get; set; }
|
||||
public virtual void Dispose()
|
||||
{
|
||||
var userIds = UsersToDelete.ToList().Select(e => e.Id);
|
||||
if (userIds.Any())
|
||||
{
|
||||
Context.GetCollection<TUser>().DeleteMany(e => userIds.Contains(e.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MongoDatabaseFixture<TUser, TRole, TKey> : MongoDatabaseFixture<TUser, TKey>, IDisposable
|
||||
where TUser : IDocument<TKey>
|
||||
where TRole : IDocument<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
public MongoDatabaseFixture()
|
||||
{
|
||||
Context = new MongoDbContext(
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.ConnectionString,
|
||||
Container.MongoDbIdentityConfiguration.MongoDbSettings.DatabaseName);
|
||||
UsersToDelete = new ConcurrentBag<TUser>();
|
||||
RolesToDelete = new ConcurrentBag<TRole>();
|
||||
}
|
||||
public ConcurrentBag<TRole> RolesToDelete { get; set; }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
var userIds = UsersToDelete.ToList().Select(e => e.Id);
|
||||
if (userIds.Any())
|
||||
{
|
||||
Context.GetCollection<TUser>().DeleteMany(e => userIds.Contains(e.Id));
|
||||
}
|
||||
var roleIds = RolesToDelete.ToList().Select(e => e.Id);
|
||||
if (roleIds.Any())
|
||||
{
|
||||
Context.GetCollection<TRole>().DeleteMany(e => roleIds.Contains(e.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+165
@@ -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.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.Test.Utilities
|
||||
{
|
||||
//public class SqlServerTestStore : IDisposable
|
||||
//{
|
||||
// public const int CommandTimeout = 90;
|
||||
|
||||
// public static string CreateConnectionString(string name)
|
||||
// {
|
||||
// var connStrBuilder = new SqlConnectionStringBuilder(TestEnvironment.Config["Test:SqlServer:DefaultConnectionString"])
|
||||
// {
|
||||
// InitialCatalog = name
|
||||
// };
|
||||
|
||||
// return connStrBuilder.ConnectionString;
|
||||
// }
|
||||
|
||||
// public static SqlServerTestStore CreateScratch(bool createDatabase = true)
|
||||
// => new SqlServerTestStore(GetScratchDbName()).CreateTransient(createDatabase);
|
||||
|
||||
// private SqlConnection _connection;
|
||||
// private readonly string _name;
|
||||
// private bool _deleteDatabase;
|
||||
|
||||
// private SqlServerTestStore(string name)
|
||||
// {
|
||||
// _name = name;
|
||||
// }
|
||||
|
||||
// private static string GetScratchDbName()
|
||||
// {
|
||||
// string name;
|
||||
// do
|
||||
// {
|
||||
// name = "Scratch_" + Guid.NewGuid();
|
||||
// } while (DatabaseExists(name)
|
||||
// || DatabaseFilesExist(name));
|
||||
|
||||
// return name;
|
||||
// }
|
||||
|
||||
// private static void WaitForExists(SqlConnection connection)
|
||||
// {
|
||||
// var retryCount = 0;
|
||||
// while (true)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// connection.Open();
|
||||
|
||||
// connection.Close();
|
||||
|
||||
// return;
|
||||
// }
|
||||
// catch (SqlException e)
|
||||
// {
|
||||
// if (++retryCount >= 30
|
||||
// || (e.Number != 233 && e.Number != -2 && e.Number != 4060))
|
||||
// {
|
||||
// throw;
|
||||
// }
|
||||
|
||||
// SqlConnection.ClearPool(connection);
|
||||
|
||||
// Thread.Sleep(100);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private SqlServerTestStore CreateTransient(bool createDatabase)
|
||||
// {
|
||||
// _connection = new SqlConnection(CreateConnectionString(_name));
|
||||
|
||||
// if (createDatabase)
|
||||
// {
|
||||
// using (var master = new SqlConnection(CreateConnectionString("master")))
|
||||
// {
|
||||
// master.Open();
|
||||
// using (var command = master.CreateCommand())
|
||||
// {
|
||||
// command.CommandTimeout = CommandTimeout;
|
||||
// command.CommandText = $"{Environment.NewLine}CREATE DATABASE [{_name}]";
|
||||
|
||||
// command.ExecuteNonQuery();
|
||||
|
||||
// WaitForExists(_connection);
|
||||
// }
|
||||
// }
|
||||
// _connection.Open();
|
||||
// }
|
||||
|
||||
// _deleteDatabase = true;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
// private static bool DatabaseExists(string name)
|
||||
// {
|
||||
// using (var master = new SqlConnection(CreateConnectionString("master")))
|
||||
// {
|
||||
// master.Open();
|
||||
|
||||
// using (var command = master.CreateCommand())
|
||||
// {
|
||||
// command.CommandTimeout = CommandTimeout;
|
||||
// command.CommandText = $@"SELECT COUNT(*) FROM sys.databases WHERE name = N'{name}'";
|
||||
|
||||
// return (int) command.ExecuteScalar() > 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private static bool DatabaseFilesExist(string name)
|
||||
// {
|
||||
// var userFolder = Environment.GetEnvironmentVariable("USERPROFILE") ??
|
||||
// Environment.GetEnvironmentVariable("HOME");
|
||||
// return userFolder != null
|
||||
// && (File.Exists(Path.Combine(userFolder, name + ".mdf"))
|
||||
// || File.Exists(Path.Combine(userFolder, name + "_log.ldf")));
|
||||
// }
|
||||
|
||||
// private void DeleteDatabase(string name)
|
||||
// {
|
||||
// using (var master = new SqlConnection(CreateConnectionString("master")))
|
||||
// {
|
||||
// master.Open();
|
||||
|
||||
// using (var command = master.CreateCommand())
|
||||
// {
|
||||
// command.CommandTimeout = CommandTimeout;
|
||||
// // Query will take a few seconds if (and only if) there are active connections
|
||||
|
||||
// // SET SINGLE_USER will close any open connections that would prevent the drop
|
||||
// command.CommandText
|
||||
// = string.Format(@"IF EXISTS (SELECT * FROM sys.databases WHERE name = N'{0}')
|
||||
// BEGIN
|
||||
// ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
|
||||
// DROP DATABASE [{0}];
|
||||
// END", name);
|
||||
|
||||
// command.ExecuteNonQuery();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// public DbConnection Connection => _connection;
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// _connection.Dispose();
|
||||
|
||||
// if (_deleteDatabase)
|
||||
// {
|
||||
// DeleteDatabase(_name);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
+13
@@ -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.Reflection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class ApiConsistencyTest : ApiConsistencyTestBase
|
||||
{
|
||||
protected override Assembly TargetAssembly => typeof(IdentityOptions).GetTypeInfo().Assembly;
|
||||
}
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
// 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.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class IdentityBuilderTest
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideUserStore()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddUserStore<MyUberThingy>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IUserStore<TestUser>>() as MyUberThingy;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideRoleStore()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddRoleStore<MyUberThingy>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleStore<TestRole>>() as MyUberThingy;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverridePrincipalFactory()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddLogging()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser, TestRole>()
|
||||
.AddClaimsPrincipalFactory<MyClaimsPrincipalFactory>()
|
||||
.AddUserManager<MyUserManager>()
|
||||
.AddUserStore<NoopUserStore>()
|
||||
.AddRoleStore<NoopRoleStore>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IUserClaimsPrincipalFactory<TestUser>>() as MyClaimsPrincipalFactory;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideRoleValidator()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddRoleValidator<MyUberThingy>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleValidator<TestRole>>() as MyUberThingy;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideUserValidator()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddUserValidator<MyUberThingy>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IUserValidator<TestUser>>() as MyUberThingy;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverridePasswordValidator()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddPasswordValidator<MyUberThingy>();
|
||||
var thingy = services.BuildServiceProvider().GetRequiredService<IPasswordValidator<TestUser>>() as MyUberThingy;
|
||||
Assert.NotNull(thingy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideUserManager()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser, TestRole>()
|
||||
.AddUserStore<NoopUserStore>()
|
||||
.AddUserManager<MyUserManager>();
|
||||
var myUserManager = services.BuildServiceProvider().GetRequiredService(typeof(UserManager<TestUser>)) as MyUserManager;
|
||||
Assert.NotNull(myUserManager);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideRoleManager()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser, TestRole>()
|
||||
.AddRoleStore<NoopRoleStore>()
|
||||
.AddRoleManager<MyRoleManager>();
|
||||
var myRoleManager = services.BuildServiceProvider().GetRequiredService<RoleManager<TestRole>>() as MyRoleManager;
|
||||
Assert.NotNull(myRoleManager);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanOverrideSignInManager()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
|
||||
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>()
|
||||
.AddLogging();
|
||||
services.AddIdentity<TestUser, TestRole>()
|
||||
.AddUserStore<NoopUserStore>()
|
||||
.AddRoleStore<NoopRoleStore>()
|
||||
.AddUserManager<MyUserManager>()
|
||||
.AddClaimsPrincipalFactory<MyClaimsPrincipalFactory>()
|
||||
.AddSignInManager<MySignInManager>();
|
||||
var myUserManager = services.BuildServiceProvider().GetRequiredService(typeof(SignInManager<TestUser>)) as MySignInManager;
|
||||
Assert.NotNull(myUserManager);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureDefaultServices()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>();
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var userValidator = provider.GetRequiredService<IUserValidator<TestUser>>() as UserValidator<TestUser>;
|
||||
Assert.NotNull(userValidator);
|
||||
|
||||
var pwdValidator = provider.GetRequiredService<IPasswordValidator<TestUser>>() as PasswordValidator<TestUser>;
|
||||
Assert.NotNull(pwdValidator);
|
||||
|
||||
var hasher = provider.GetRequiredService<IPasswordHasher<TestUser>>() as PasswordHasher<TestUser>;
|
||||
Assert.NotNull(hasher);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureDefaultTokenProviders()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddIdentity<TestUser,TestRole>().AddDefaultTokenProviders();
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var tokenProviders = provider.GetRequiredService<IOptions<IdentityOptions>>().Value.Tokens.ProviderMap.Values;
|
||||
Assert.Equal(4, tokenProviders.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddManagerWithWrongTypesThrows()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
var builder = services.AddIdentity<TestUser, TestRole>();
|
||||
Assert.Throws<InvalidOperationException>(() => builder.AddUserManager<object>());
|
||||
Assert.Throws<InvalidOperationException>(() => builder.AddRoleManager<object>());
|
||||
Assert.Throws<InvalidOperationException>(() => builder.AddSignInManager<object>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTokenProviderWithWrongTypesThrows()
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
var builder = services.AddIdentity<TestUser, TestRole>();
|
||||
Assert.Throws<InvalidOperationException>(() => builder.AddTokenProvider<object>("whatevs"));
|
||||
Assert.Throws<InvalidOperationException>(() => builder.AddTokenProvider("whatevs", typeof(object)));
|
||||
}
|
||||
|
||||
private class MyUberThingy : IUserValidator<TestUser>, IPasswordValidator<TestUser>, IRoleValidator<TestRole>, IUserStore<TestUser>, IRoleStore<TestRole>
|
||||
{
|
||||
public Task<IdentityResult> CreateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<TestUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<TestUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRoleIdAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetUserIdAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedRoleNameAsync(TestRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedUserNameAsync(TestUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetRoleNameAsync(TestRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(RoleManager<TestRole> manager, TestRole role)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(UserManager<TestUser> manager, TestUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(UserManager<TestUser> manager, TestUser user, string password)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<TestRole> IRoleStore<TestRole>.FindByIdAsync(string roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<TestRole> IRoleStore<TestRole>.FindByNameAsync(string roleName, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class MySignInManager : SignInManager<TestUser>
|
||||
{
|
||||
public MySignInManager(UserManager<TestUser> manager, IHttpContextAccessor context, IUserClaimsPrincipalFactory<TestUser> claimsFactory) : base(manager, context, claimsFactory, null, null, null) { }
|
||||
}
|
||||
|
||||
private class MyUserManager : UserManager<TestUser>
|
||||
{
|
||||
public MyUserManager(IUserStore<TestUser> store) : base(store, null, null, null, null, null, null, null, null) { }
|
||||
}
|
||||
|
||||
private class MyClaimsPrincipalFactory : UserClaimsPrincipalFactory<TestUser, TestRole>
|
||||
{
|
||||
public MyClaimsPrincipalFactory(UserManager<TestUser> userManager, RoleManager<TestRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class MyRoleManager : RoleManager<TestRole>
|
||||
{
|
||||
public MyRoleManager(IRoleStore<TestRole> store,
|
||||
IEnumerable<IRoleValidator<TestRole>> roleValidators) : base(store, null, null, null, null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// 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.Security.Claims;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class IdentityOptionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void VerifyDefaultOptions()
|
||||
{
|
||||
var options = new IdentityOptions();
|
||||
Assert.True(options.Lockout.AllowedForNewUsers);
|
||||
Assert.Equal(TimeSpan.FromMinutes(5), options.Lockout.DefaultLockoutTimeSpan);
|
||||
Assert.Equal(5, options.Lockout.MaxFailedAccessAttempts);
|
||||
|
||||
Assert.True(options.Password.RequireDigit);
|
||||
Assert.True(options.Password.RequireLowercase);
|
||||
Assert.True(options.Password.RequireNonAlphanumeric);
|
||||
Assert.True(options.Password.RequireUppercase);
|
||||
Assert.Equal(6, options.Password.RequiredLength);
|
||||
Assert.Equal(1, options.Password.RequiredUniqueChars);
|
||||
|
||||
Assert.Equal("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", options.User.AllowedUserNameCharacters);
|
||||
Assert.False(options.User.RequireUniqueEmail);
|
||||
|
||||
Assert.Equal(ClaimTypes.Role, options.ClaimsIdentity.RoleClaimType);
|
||||
Assert.Equal(ClaimTypes.Name, options.ClaimsIdentity.UserNameClaimType);
|
||||
Assert.Equal(ClaimTypes.NameIdentifier, options.ClaimsIdentity.UserIdClaimType);
|
||||
Assert.Equal("AspNet.Identity.SecurityStamp", options.ClaimsIdentity.SecurityStampClaimType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCustomizeIdentityOptions()
|
||||
{
|
||||
var services = new ServiceCollection().Configure<IdentityOptions>(options => options.Password.RequiredLength = -1);
|
||||
services.AddIdentity<TestUser,TestRole>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var setup = serviceProvider.GetRequiredService<IConfigureOptions<IdentityOptions>>();
|
||||
Assert.NotNull(setup);
|
||||
var optionsGetter = serviceProvider.GetRequiredService<IOptions<IdentityOptions>>();
|
||||
Assert.NotNull(optionsGetter);
|
||||
var myOptions = optionsGetter.Value;
|
||||
Assert.True(myOptions.Password.RequireLowercase);
|
||||
Assert.True(myOptions.Password.RequireDigit);
|
||||
Assert.True(myOptions.Password.RequireNonAlphanumeric);
|
||||
Assert.True(myOptions.Password.RequireUppercase);
|
||||
Assert.Equal(1, myOptions.Password.RequiredUniqueChars);
|
||||
Assert.Equal(-1, myOptions.Password.RequiredLength);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSetupIdentityOptions()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddIdentity<TestUser,TestRole>(options => options.User.RequireUniqueEmail = true);
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var optionsGetter = serviceProvider.GetRequiredService<IOptions<IdentityOptions>>();
|
||||
Assert.NotNull(optionsGetter);
|
||||
|
||||
var myOptions = optionsGetter.Value;
|
||||
Assert.True(myOptions.User.RequireUniqueEmail);
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -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.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class IdentityResultTest
|
||||
{
|
||||
[Fact]
|
||||
public void VerifyDefaultConstructor()
|
||||
{
|
||||
var result = new IdentityResult();
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullFailedUsesEmptyErrors()
|
||||
{
|
||||
var result = IdentityResult.Failed();
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class NoopRoleStore : IRoleStore<TestRole>
|
||||
{
|
||||
public Task<IdentityResult> CreateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<string> GetRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task SetRoleNameAsync(TestRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<TestRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<TestRole>(null);
|
||||
}
|
||||
|
||||
public Task<TestRole> FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<TestRole>(null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<string> GetRoleIdAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task SetNormalizedRoleNameAsync(TestRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -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;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class NoopUserStore : IUserStore<TestUser>
|
||||
{
|
||||
public Task<string> GetUserIdAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.Id);
|
||||
}
|
||||
|
||||
public Task<string> GetUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(user.UserName);
|
||||
}
|
||||
|
||||
public Task SetUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<TestUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<TestUser>(null);
|
||||
}
|
||||
|
||||
public Task<TestUser> FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<TestUser>(null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task SetNormalizedUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -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.Cryptography;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class PasswordHasherTest
|
||||
{
|
||||
[Fact]
|
||||
public void Ctor_InvalidCompatMode_Throws()
|
||||
{
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
new PasswordHasher(compatMode: (PasswordHasherCompatibilityMode)(-1));
|
||||
});
|
||||
Assert.Equal("The provided PasswordHasherCompatibilityMode is invalid.", ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
public void Ctor_InvalidIterCount_Throws(int iterCount)
|
||||
{
|
||||
// Act & assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
new PasswordHasher(iterCount: iterCount);
|
||||
});
|
||||
Assert.Equal("The iteration count must be a positive integer.", ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PasswordHasherCompatibilityMode.IdentityV2)]
|
||||
[InlineData(PasswordHasherCompatibilityMode.IdentityV3)]
|
||||
public void FullRoundTrip(PasswordHasherCompatibilityMode compatMode)
|
||||
{
|
||||
// Arrange
|
||||
var hasher = new PasswordHasher(compatMode: compatMode);
|
||||
|
||||
// Act & assert - success case
|
||||
var hashedPassword = hasher.HashPassword(null, "password 1");
|
||||
var successResult = hasher.VerifyHashedPassword(null, hashedPassword, "password 1");
|
||||
Assert.Equal(PasswordVerificationResult.Success, successResult);
|
||||
|
||||
// Act & assert - failure case
|
||||
var failedResult = hasher.VerifyHashedPassword(null, hashedPassword, "password 2");
|
||||
Assert.Equal(PasswordVerificationResult.Failed, failedResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Version 2 payloads
|
||||
[InlineData("AAABAgMEBQYHCAkKCwwNDg+uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALtH1uJg==")] // incorrect password
|
||||
[InlineData("AAABAgMEBQYHCAkKCwwNDg+ukCEMDf0yyQ29NYubggE=")] // too short
|
||||
[InlineData("AAABAgMEBQYHCAkKCwwNDg+ukCEMDf0yyQ29NYubggHIVY0sdEUfdyeM+E1LtH1uJgAAAAAAAAAAAAA=")] // extra data at end
|
||||
// Version 3 payloads
|
||||
[InlineData("AQAAAAAAAAD6AAAAEAhftMyfTJyAAAAAAAAAAAAAAAAAAAih5WsjXaR3PA9M")] // incorrect password
|
||||
[InlineData("AQAAAAIAAAAyAAAAEOMwvh3+FZxqkdMBz2ekgGhwQ4A=")] // too short
|
||||
[InlineData("AQAAAAIAAAAyAAAAEOMwvh3+FZxqkdMBz2ekgGhwQ4B6pZWND6zgESBuWiHwAAAAAAAAAAAA")] // extra data at end
|
||||
public void VerifyHashedPassword_FailureCases(string hashedPassword)
|
||||
{
|
||||
// Arrange
|
||||
var hasher = new PasswordHasher();
|
||||
|
||||
// Act
|
||||
var result = hasher.VerifyHashedPassword(null, hashedPassword, "my password");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(PasswordVerificationResult.Failed, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Version 2 payloads
|
||||
[InlineData("ANXrDknc7fGPpigibZXXZFMX4aoqz44JveK6jQuwY3eH/UyPhvr5xTPeGYEckLxz9A==")] // SHA1, 1000 iterations, 128-bit salt, 256-bit subkey
|
||||
// Version 3 payloads
|
||||
[InlineData("AQAAAAIAAAAyAAAAEOMwvh3+FZxqkdMBz2ekgGhwQ4B6pZWND6zgESBuWiHw")] // SHA512, 50 iterations, 128-bit salt, 128-bit subkey
|
||||
[InlineData("AQAAAAIAAAD6AAAAIJbVi5wbMR+htSfFp8fTw8N8GOS/Sje+S/4YZcgBfU7EQuqv4OkVYmc4VJl9AGZzmRTxSkP7LtVi9IWyUxX8IAAfZ8v+ZfhjCcudtC1YERSqE1OEdXLW9VukPuJWBBjLuw==")] // SHA512, 250 iterations, 256-bit salt, 512-bit subkey
|
||||
[InlineData("AQAAAAAAAAD6AAAAEAhftMyfTJylOlZT+eEotFXd1elee8ih5WsjXaR3PA9M")] // SHA1, 250 iterations, 128-bit salt, 128-bit subkey
|
||||
[InlineData("AQAAAAEAA9CQAAAAIESkQuj2Du8Y+kbc5lcN/W/3NiAZFEm11P27nrSN5/tId+bR1SwV8CO1Jd72r4C08OLvplNlCDc3oQZ8efcW+jQ=")] // SHA256, 250000 iterations, 256-bit salt, 256-bit subkey
|
||||
public void VerifyHashedPassword_Version2CompatMode_SuccessCases(string hashedPassword)
|
||||
{
|
||||
// Arrange
|
||||
var hasher = new PasswordHasher(compatMode: PasswordHasherCompatibilityMode.IdentityV2);
|
||||
|
||||
// Act
|
||||
var result = hasher.VerifyHashedPassword(null, hashedPassword, "my password");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(PasswordVerificationResult.Success, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Version 2 payloads
|
||||
[InlineData("ANXrDknc7fGPpigibZXXZFMX4aoqz44JveK6jQuwY3eH/UyPhvr5xTPeGYEckLxz9A==", PasswordVerificationResult.SuccessRehashNeeded)] // SHA1, 1000 iterations, 128-bit salt, 256-bit subkey
|
||||
// Version 3 payloads
|
||||
[InlineData("AQAAAAIAAAAyAAAAEOMwvh3+FZxqkdMBz2ekgGhwQ4B6pZWND6zgESBuWiHw", PasswordVerificationResult.SuccessRehashNeeded)] // SHA512, 50 iterations, 128-bit salt, 128-bit subkey
|
||||
[InlineData("AQAAAAIAAAD6AAAAIJbVi5wbMR+htSfFp8fTw8N8GOS/Sje+S/4YZcgBfU7EQuqv4OkVYmc4VJl9AGZzmRTxSkP7LtVi9IWyUxX8IAAfZ8v+ZfhjCcudtC1YERSqE1OEdXLW9VukPuJWBBjLuw==", PasswordVerificationResult.SuccessRehashNeeded)] // SHA512, 250 iterations, 256-bit salt, 512-bit subkey
|
||||
[InlineData("AQAAAAAAAAD6AAAAEAhftMyfTJylOlZT+eEotFXd1elee8ih5WsjXaR3PA9M", PasswordVerificationResult.SuccessRehashNeeded)] // SHA1, 250 iterations, 128-bit salt, 128-bit subkey
|
||||
[InlineData("AQAAAAEAA9CQAAAAIESkQuj2Du8Y+kbc5lcN/W/3NiAZFEm11P27nrSN5/tId+bR1SwV8CO1Jd72r4C08OLvplNlCDc3oQZ8efcW+jQ=", PasswordVerificationResult.Success)] // SHA256, 250000 iterations, 256-bit salt, 256-bit subkey
|
||||
public void VerifyHashedPassword_Version3CompatMode_SuccessCases(string hashedPassword, PasswordVerificationResult expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var hasher = new PasswordHasher(compatMode: PasswordHasherCompatibilityMode.IdentityV3);
|
||||
|
||||
// Act
|
||||
var actualResult = hasher.VerifyHashedPassword(null, hashedPassword, "my password");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
private sealed class PasswordHasher : PasswordHasher<object>
|
||||
{
|
||||
public PasswordHasher(PasswordHasherCompatibilityMode? compatMode = null, int? iterCount = null)
|
||||
: base(BuildOptions(compatMode, iterCount))
|
||||
{
|
||||
}
|
||||
|
||||
private static IOptions<PasswordHasherOptions> BuildOptions(PasswordHasherCompatibilityMode? compatMode, int? iterCount)
|
||||
{
|
||||
var options = new PasswordHasherOptionsAccessor();
|
||||
if (compatMode != null)
|
||||
{
|
||||
options.Value.CompatibilityMode = (PasswordHasherCompatibilityMode)compatMode;
|
||||
}
|
||||
if (iterCount != null)
|
||||
{
|
||||
options.Value.IterationCount = (int)iterCount;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SequentialRandomNumberGenerator : RandomNumberGenerator
|
||||
{
|
||||
private byte _value;
|
||||
|
||||
public override void GetBytes(byte[] data)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] = _value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PasswordHasherOptionsAccessor : IOptions<PasswordHasherOptions>
|
||||
{
|
||||
public PasswordHasherOptions Value { get; } = new PasswordHasherOptions();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
// 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;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class PasswordValidatorTest
|
||||
{
|
||||
[Flags]
|
||||
public enum Errors
|
||||
{
|
||||
None = 0,
|
||||
Length = 2,
|
||||
Alpha = 4,
|
||||
Upper = 8,
|
||||
Lower = 16,
|
||||
Digit = 32,
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateThrowsWithNullTest()
|
||||
{
|
||||
// Setup
|
||||
var validator = new PasswordValidator<TestUser>();
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync(null, null, "foo"));
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("abc")]
|
||||
[InlineData("abcde")]
|
||||
public async Task FailsIfTooShortTests(string input)
|
||||
{
|
||||
const string error = "Passwords must be at least 6 characters.";
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = false;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(manager, null, input), error);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("abcdef")]
|
||||
[InlineData("aaaaaaaaaaa")]
|
||||
public async Task SuccessIfLongEnoughTests(string input)
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = false;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(manager, null, input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("a")]
|
||||
[InlineData("aaaaaaaaaaa")]
|
||||
public async Task FailsWithoutRequiredNonAlphanumericTests(string input)
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = true;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
manager.Options.Password.RequiredLength = 0;
|
||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(manager, null, input),
|
||||
"Passwords must have at least one non alphanumeric character.");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("@")]
|
||||
[InlineData("abcd@e!ld!kajfd")]
|
||||
[InlineData("!!!!!!")]
|
||||
public async Task SucceedsWithRequiredNonAlphanumericTests(string input)
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = true;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
manager.Options.Password.RequiredLength = 0;
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(manager, null, input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("a", 2)]
|
||||
[InlineData("aaaaaaaaaaa", 2)]
|
||||
[InlineData("abcdabcdabcdabcdabcdabcdabcd", 5)]
|
||||
public async Task FailsWithoutRequiredUniqueCharsTests(string input, int uniqueChars)
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = false;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
manager.Options.Password.RequiredLength = 0;
|
||||
manager.Options.Password.RequiredUniqueChars = uniqueChars;
|
||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(manager, null, input),
|
||||
String.Format("Passwords must use at least {0} different characters.", uniqueChars));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("12345", 5)]
|
||||
[InlineData("aAbBc", 5)]
|
||||
[InlineData("aAbBcaAbBcaAbBc", 5)]
|
||||
[InlineData("!@#$%", 5)]
|
||||
[InlineData("a", 1)]
|
||||
[InlineData("this is a long password with many chars", 10)]
|
||||
public async Task SucceedsWithRequiredUniqueCharsTests(string input, int uniqueChars)
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
manager.Options.Password.RequireUppercase = false;
|
||||
manager.Options.Password.RequireNonAlphanumeric = false;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
manager.Options.Password.RequiredLength = 0;
|
||||
manager.Options.Password.RequiredUniqueChars = uniqueChars;
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(manager, null, input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("abcde", Errors.Length | Errors.Alpha | Errors.Upper | Errors.Digit)]
|
||||
[InlineData("a@B@cd", Errors.Digit)]
|
||||
[InlineData("___", Errors.Length | Errors.Digit | Errors.Lower | Errors.Upper)]
|
||||
[InlineData("a_b9de", Errors.Upper)]
|
||||
[InlineData("abcd@e!ld!kaj9Fd", Errors.None)]
|
||||
[InlineData("aB1@df", Errors.None)]
|
||||
public async Task UberMixedRequiredTests(string input, Errors errorMask)
|
||||
{
|
||||
const string alphaError = "Passwords must have at least one non alphanumeric character.";
|
||||
const string upperError = "Passwords must have at least one uppercase ('A'-'Z').";
|
||||
const string lowerError = "Passwords must have at least one lowercase ('a'-'z').";
|
||||
const string digitError = "Passwords must have at least one digit ('0'-'9').";
|
||||
const string lengthError = "Passwords must be at least 6 characters.";
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var valid = new PasswordValidator<TestUser>();
|
||||
var errors = new List<string>();
|
||||
if ((errorMask & Errors.Length) != Errors.None)
|
||||
{
|
||||
errors.Add(lengthError);
|
||||
}
|
||||
if ((errorMask & Errors.Alpha) != Errors.None)
|
||||
{
|
||||
errors.Add(alphaError);
|
||||
}
|
||||
if ((errorMask & Errors.Digit) != Errors.None)
|
||||
{
|
||||
errors.Add(digitError);
|
||||
}
|
||||
if ((errorMask & Errors.Lower) != Errors.None)
|
||||
{
|
||||
errors.Add(lowerError);
|
||||
}
|
||||
if ((errorMask & Errors.Upper) != Errors.None)
|
||||
{
|
||||
errors.Add(upperError);
|
||||
}
|
||||
var result = await valid.ValidateAsync(manager, null, input);
|
||||
if (errors.Count == 0)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdentityResultAssert.IsFailure(result);
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Assert.Contains(result.Errors, e => e.Description == error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -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;
|
||||
using System.Security.Claims;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class ClaimsIdentityExtensionsTest
|
||||
{
|
||||
public const string ExternalAuthenticationScheme = "TestExternalAuth";
|
||||
|
||||
[Fact]
|
||||
public void IdentityExtensionsFindFirstValueNullIfUnknownTest()
|
||||
{
|
||||
var id = CreateTestExternalIdentity();
|
||||
Assert.Null(id.FindFirstValue("bogus"));
|
||||
}
|
||||
|
||||
private static ClaimsPrincipal CreateTestExternalIdentity()
|
||||
{
|
||||
return new ClaimsPrincipal(new ClaimsIdentity(
|
||||
new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "NameIdentifier", null, ExternalAuthenticationScheme),
|
||||
new Claim(ClaimTypes.Name, "Name")
|
||||
},
|
||||
ExternalAuthenticationScheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
// 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 Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class RoleManagerTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateCallsStore()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IRoleStore<TestRole>>();
|
||||
var role = new TestRole { Name = "Foo" };
|
||||
store.Setup(s => s.CreateAsync(role, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
store.Setup(s => s.GetRoleNameAsync(role, CancellationToken.None)).Returns(Task.FromResult(role.Name)).Verifiable();
|
||||
store.Setup(s => s.SetNormalizedRoleNameAsync(role, role.Name.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
||||
var roleManager = MockHelpers.TestRoleManager(store.Object);
|
||||
|
||||
// Act
|
||||
var result = await roleManager.CreateAsync(role);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
store.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateCallsStore()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IRoleStore<TestRole>>();
|
||||
var role = new TestRole { Name = "Foo" };
|
||||
store.Setup(s => s.UpdateAsync(role, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
store.Setup(s => s.GetRoleNameAsync(role, CancellationToken.None)).Returns(Task.FromResult(role.Name)).Verifiable();
|
||||
store.Setup(s => s.SetNormalizedRoleNameAsync(role, role.Name.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
||||
var roleManager = MockHelpers.TestRoleManager(store.Object);
|
||||
|
||||
// Act
|
||||
var result = await roleManager.UpdateAsync(role);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
store.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RolesQueryableFailWhenStoreNotImplemented()
|
||||
{
|
||||
var manager = CreateRoleManager(new NoopRoleStore());
|
||||
Assert.False(manager.SupportsQueryableRoles);
|
||||
Assert.Throws<NotSupportedException>(() => manager.Roles.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FindByNameCallsStoreWithNormalizedName()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IRoleStore<TestRole>>();
|
||||
var role = new TestRole { Name = "Foo" };
|
||||
store.Setup(s => s.FindByNameAsync("FOO", CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
|
||||
var manager = MockHelpers.TestRoleManager(store.Object);
|
||||
|
||||
// Act
|
||||
var result = await manager.FindByNameAsync(role.Name);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(role, result);
|
||||
store.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanFindByNameCallsStoreWithoutNormalizedName()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IRoleStore<TestRole>>();
|
||||
var role = new TestRole { Name = "Foo" };
|
||||
store.Setup(s => s.FindByNameAsync(role.Name, CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
|
||||
var manager = MockHelpers.TestRoleManager(store.Object);
|
||||
manager.KeyNormalizer = null;
|
||||
|
||||
// Act
|
||||
var result = await manager.FindByNameAsync(role.Name);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(role, result);
|
||||
store.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RoleExistsCallsStoreWithNormalizedName()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IRoleStore<TestRole>>();
|
||||
var role = new TestRole { Name = "Foo" };
|
||||
store.Setup(s => s.FindByNameAsync("FOO", CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
|
||||
var manager = MockHelpers.TestRoleManager(store.Object);
|
||||
|
||||
// Act
|
||||
var result = await manager.RoleExistsAsync(role.Name);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
store.VerifyAll();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DisposeAfterDisposeDoesNotThrow()
|
||||
{
|
||||
var manager = CreateRoleManager(new NoopRoleStore());
|
||||
manager.Dispose();
|
||||
manager.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RoleManagerPublicNullChecks()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>("store",
|
||||
() => new RoleManager<TestRole>(null, null, null, null, null));
|
||||
var manager = CreateRoleManager(new NotImplementedStore());
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await manager.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await manager.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await manager.DeleteAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("roleName", async () => await manager.FindByNameAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("roleName", async () => await manager.RoleExistsAsync(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RoleStoreMethodsThrowWhenDisposed()
|
||||
{
|
||||
var manager = CreateRoleManager(new NoopRoleStore());
|
||||
manager.Dispose();
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.FindByIdAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.FindByNameAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.RoleExistsAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.DeleteAsync(null));
|
||||
}
|
||||
|
||||
private static RoleManager<TestRole> CreateRoleManager(IRoleStore<TestRole> roleStore)
|
||||
{
|
||||
return MockHelpers.TestRoleManager(roleStore);
|
||||
}
|
||||
|
||||
private class NotImplementedStore : IRoleStore<TestRole>
|
||||
{
|
||||
public Task<IdentityResult> CreateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> UpdateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IdentityResult> DeleteAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRoleIdAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetRoleNameAsync(TestRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<TestRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<TestRole> FindByNameAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetNormalizedRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SetNormalizedRoleNameAsync(TestRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class RoleValidatorTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ValidateThrowsWithNull()
|
||||
{
|
||||
// Setup
|
||||
var validator = new RoleValidator<TestRole>();
|
||||
var manager = MockHelpers.TestRoleManager<TestRole>();
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("manager", async () => await validator.ValidateAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await validator.ValidateAsync(manager, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public async Task ValidateFailsWithTooShortRoleName(string input)
|
||||
{
|
||||
// Setup
|
||||
var validator = new RoleValidator<TestRole>();
|
||||
var manager = MockHelpers.TestRoleManager<TestRole>();
|
||||
var user = new TestRole {Name = input};
|
||||
|
||||
// Act
|
||||
var result = await validator.ValidateAsync(manager, user);
|
||||
|
||||
// Assert
|
||||
IdentityResultAssert.IsFailure(result, new IdentityErrorDescriber().InvalidRoleName(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// 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.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class SecurityStampTest
|
||||
{
|
||||
private class NoopHandler : IAuthenticationHandler
|
||||
{
|
||||
public Task<AuthenticateResult> AuthenticateAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task ChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task ForbidAsync(AuthenticationProperties properties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> HandleRequestAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SignOutAsync(AuthenticationProperties properties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnValidatePrincipalThrowsWithEmptyServiceCollection()
|
||||
{
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
httpContext.Setup(c => c.RequestServices).Returns(new ServiceCollection().BuildServiceProvider());
|
||||
var id = new ClaimsPrincipal(new ClaimsIdentity(IdentityConstants.ApplicationScheme));
|
||||
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, IdentityConstants.ApplicationScheme);
|
||||
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => SecurityStampValidator.ValidatePrincipalAsync(context));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task OnValidatePrincipalTestSuccess(bool isPersistent)
|
||||
{
|
||||
var user = new TestUser("test");
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>();
|
||||
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
|
||||
var identityOptions = new Mock<IOptions<IdentityOptions>>();
|
||||
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions());
|
||||
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
|
||||
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object);
|
||||
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
|
||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
var principal = new ClaimsPrincipal(id);
|
||||
|
||||
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1), IsPersistent = isPersistent };
|
||||
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
|
||||
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable();
|
||||
signInManager.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(options.Object);
|
||||
services.AddSingleton(signInManager.Object);
|
||||
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
|
||||
var ticket = new AuthenticationTicket(principal,
|
||||
properties,
|
||||
IdentityConstants.ApplicationScheme);
|
||||
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
|
||||
Assert.NotNull(context.Properties);
|
||||
Assert.NotNull(context.Options);
|
||||
Assert.NotNull(context.Principal);
|
||||
await
|
||||
SecurityStampValidator.ValidatePrincipalAsync(context);
|
||||
Assert.NotNull(context.Principal);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
|
||||
{
|
||||
var user = new TestUser("test");
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>();
|
||||
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
|
||||
var identityOptions = new Mock<IOptions<IdentityOptions>>();
|
||||
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions());
|
||||
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
|
||||
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object);
|
||||
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
|
||||
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(options.Object);
|
||||
services.AddSingleton(signInManager.Object);
|
||||
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
|
||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
|
||||
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
|
||||
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) },
|
||||
IdentityConstants.ApplicationScheme);
|
||||
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
|
||||
Assert.NotNull(context.Properties);
|
||||
Assert.NotNull(context.Options);
|
||||
Assert.NotNull(context.Principal);
|
||||
await SecurityStampValidator.ValidatePrincipalAsync(context);
|
||||
Assert.Null(context.Principal);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnValidateIdentityRejectsWhenNoIssuedUtc()
|
||||
{
|
||||
var user = new TestUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>();
|
||||
var identityOptions = new Mock<IOptions<IdentityOptions>>();
|
||||
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions());
|
||||
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
|
||||
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
|
||||
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object);
|
||||
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
|
||||
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(options.Object);
|
||||
services.AddSingleton(signInManager.Object);
|
||||
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
|
||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
|
||||
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
|
||||
new AuthenticationProperties(),
|
||||
IdentityConstants.ApplicationScheme);
|
||||
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
|
||||
Assert.NotNull(context.Properties);
|
||||
Assert.NotNull(context.Options);
|
||||
Assert.NotNull(context.Principal);
|
||||
await SecurityStampValidator.ValidatePrincipalAsync(context);
|
||||
Assert.Null(context.Principal);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired()
|
||||
{
|
||||
var user = new TestUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>();
|
||||
var identityOptions = new Mock<IOptions<IdentityOptions>>();
|
||||
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions());
|
||||
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
|
||||
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
|
||||
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.FromDays(1) });
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object);
|
||||
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
|
||||
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).Throws(new Exception("Shouldn't be called"));
|
||||
signInManager.Setup(s => s.SignInAsync(user, false, null)).Throws(new Exception("Shouldn't be called"));
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(options.Object);
|
||||
services.AddSingleton(signInManager.Object);
|
||||
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
|
||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||
|
||||
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
|
||||
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow },
|
||||
IdentityConstants.ApplicationScheme);
|
||||
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
|
||||
Assert.NotNull(context.Properties);
|
||||
Assert.NotNull(context.Options);
|
||||
Assert.NotNull(context.Principal);
|
||||
await SecurityStampValidator.ValidatePrincipalAsync(context);
|
||||
Assert.NotNull(context.Principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
+866
@@ -0,0 +1,866 @@
|
||||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class SignManagerInTest
|
||||
{
|
||||
//[Theory]
|
||||
//[InlineData(true)]
|
||||
//[InlineData(false)]
|
||||
//public async Task VerifyAccountControllerSignInFunctional(bool isPersistent)
|
||||
//{
|
||||
// var app = new ApplicationBuilder(new ServiceCollection().BuildServiceProvider());
|
||||
// app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
// {
|
||||
// AuthenticationScheme = ClaimsIdentityOptions.DefaultAuthenticationScheme
|
||||
// });
|
||||
|
||||
// TODO: how to functionally test context?
|
||||
// var context = new DefaultHttpContext(new FeatureCollection());
|
||||
// var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
// contextAccessor.Setup(a => a.Value).Returns(context);
|
||||
// app.UseServices(services =>
|
||||
// {
|
||||
// services.AddSingleton(contextAccessor.Object);
|
||||
// services.AddSingleton<Ilogger>(new Nulllogger());
|
||||
// services.AddIdentity<ApplicationUser, IdentityRole>(s =>
|
||||
// {
|
||||
// s.AddUserStore(() => new InMemoryUserStore<ApplicationUser>());
|
||||
// s.AddUserManager<ApplicationUserManager>();
|
||||
// s.AddRoleStore(() => new InMemoryRoleStore<IdentityRole>());
|
||||
// s.AddRoleManager<ApplicationRoleManager>();
|
||||
// });
|
||||
// services.AddTransient<ApplicationHttpSignInManager>();
|
||||
// });
|
||||
|
||||
// // Act
|
||||
// var user = new ApplicationUser
|
||||
// {
|
||||
// UserName = "Yolo"
|
||||
// };
|
||||
// const string password = "Yol0Sw@g!";
|
||||
// var userManager = app.ApplicationServices.GetRequiredService<ApplicationUserManager>();
|
||||
// var HttpSignInManager = app.ApplicationServices.GetRequiredService<ApplicationHttpSignInManager>();
|
||||
|
||||
// IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
|
||||
// var result = await HttpSignInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
|
||||
|
||||
// // Assert
|
||||
// Assert.Equal(SignInStatus.Success, result);
|
||||
// contextAccessor.Verify();
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void ConstructorNullChecks()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<TestUser>(null, null, null, null, null, null));
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>().Object;
|
||||
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<TestUser>(userManager, null, null, null, null, null));
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
var context = new Mock<HttpContext>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
|
||||
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<TestUser>(userManager, contextAccessor.Object, null, null, null, null));
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public async Task EnsureClaimsPrincipalFactoryCreateIdentityCalled()
|
||||
//{
|
||||
// // Setup
|
||||
// var user = new TestUser { UserName = "Foo" };
|
||||
// var userManager = MockHelpers.TestUserManager<TestUser>();
|
||||
// var identityFactory = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
|
||||
// const string authType = "Test";
|
||||
// var testIdentity = new ClaimsPrincipal();
|
||||
// identityFactory.Setup(s => s.CreateAsync(user)).ReturnsAsync(testIdentity).Verifiable();
|
||||
// var context = new Mock<HttpContext>();
|
||||
// var response = new Mock<HttpResponse>();
|
||||
// context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
||||
// response.Setup(r => r.SignIn(testIdentity, It.IsAny<AuthenticationProperties>())).Verifiable();
|
||||
// var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
// contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||
// var helper = new HttpAuthenticationManager(contextAccessor.Object);
|
||||
|
||||
// // Act
|
||||
// helper.SignIn(user, false);
|
||||
|
||||
// // Assert
|
||||
// identityFactory.Verify();
|
||||
// context.Verify();
|
||||
// contextAccessor.Verify();
|
||||
// response.Verify();
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public async Task PasswordSignInReturnsLockedOutWhenLockedOut()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
|
||||
|
||||
var context = new Mock<HttpContext>();
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
options.Setup(a => a.Value).Returns(identityOptions);
|
||||
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
|
||||
var logStore = new StringBuilder();
|
||||
var logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore);
|
||||
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.True(result.IsLockedOut);
|
||||
Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString());
|
||||
manager.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckPasswordSignInReturnsLockedOutWhenLockedOut()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
|
||||
|
||||
var context = new Mock<HttpContext>();
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
options.Setup(a => a.Value).Returns(identityOptions);
|
||||
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
|
||||
var logStore = new StringBuilder();
|
||||
var logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore);
|
||||
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
|
||||
// Act
|
||||
var result = await helper.CheckPasswordSignInAsync(user, "bogus", false);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.True(result.IsLockedOut);
|
||||
Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString());
|
||||
manager.Verify();
|
||||
}
|
||||
|
||||
private static Mock<UserManager<TestUser>> SetupUserManager(TestUser user)
|
||||
{
|
||||
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||
manager.Setup(m => m.FindByNameAsync(user.UserName)).ReturnsAsync(user);
|
||||
manager.Setup(m => m.FindByIdAsync(user.Id)).ReturnsAsync(user);
|
||||
manager.Setup(m => m.GetUserIdAsync(user)).ReturnsAsync(user.Id.ToString());
|
||||
manager.Setup(m => m.GetUserNameAsync(user)).ReturnsAsync(user.UserName);
|
||||
return manager;
|
||||
}
|
||||
|
||||
private static SignInManager<TestUser> SetupSignInManager(UserManager<TestUser> manager, HttpContext context, StringBuilder logStore = null, IdentityOptions identityOptions = null)
|
||||
{
|
||||
var contextAccessor = new Mock<IHttpContextAccessor>();
|
||||
contextAccessor.Setup(a => a.HttpContext).Returns(context);
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
identityOptions = identityOptions ?? new IdentityOptions();
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
options.Setup(a => a.Value).Returns(identityOptions);
|
||||
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager, roleManager.Object, options.Object);
|
||||
var sm = new SignInManager<TestUser>(manager, contextAccessor.Object, claimsFactory, options.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
sm.Logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore ?? new StringBuilder()).Object;
|
||||
return sm;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CanPasswordSignIn(bool isPersistent)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
SetupSignIn(context, auth, user.Id, isPersistent);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanPasswordSignInWithNoLogger()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
SetupSignIn(context, auth, user.Id, false);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task PasswordSignInWorksWithNonTwoFactorStore()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
SetupSignIn(context, auth);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task PasswordSignInRequiresVerification(bool supportsLockout)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(supportsLockout).Verifiable();
|
||||
if (supportsLockout)
|
||||
{
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
}
|
||||
IList<string> providers = new List<string>();
|
||||
providers.Add("PhoneNumber");
|
||||
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).Returns(Task.FromResult(providers)).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
if (supportsLockout)
|
||||
{
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
}
|
||||
var context = new DefaultHttpContext();
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
var auth = MockAuth(context);
|
||||
auth.Setup(a => a.SignInAsync(context, IdentityConstants.TwoFactorUserIdScheme,
|
||||
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id),
|
||||
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.True(result.RequiresTwoFactor);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task ExternalSignInRequiresVerificationIfNotBypassed(bool bypass)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
const string loginProvider = "login";
|
||||
const string providerKey = "fookey";
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(false).Verifiable();
|
||||
manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable();
|
||||
if (!bypass)
|
||||
{
|
||||
IList<string> providers = new List<string>();
|
||||
providers.Add("PhoneNumber");
|
||||
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).Returns(Task.FromResult(providers)).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable();
|
||||
}
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
|
||||
if (bypass)
|
||||
{
|
||||
SetupSignIn(context, auth, user.Id, false, loginProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
auth.Setup(a => a.SignInAsync(context, IdentityConstants.TwoFactorUserIdScheme,
|
||||
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id),
|
||||
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
}
|
||||
|
||||
// Act
|
||||
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent: false, bypassTwoFactor: bypass);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(bypass, result.Succeeded);
|
||||
Assert.Equal(!bypass, result.RequiresTwoFactor);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
private class GoodTokenProvider : AuthenticatorTokenProvider<TestUser>
|
||||
{
|
||||
public override Task<bool> ValidateAsync(string purpose, string token, UserManager<TestUser> manager, TestUser user)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[Theory]
|
||||
//[InlineData(null, true, true)]
|
||||
//[InlineData("Authenticator", false, true)]
|
||||
//[InlineData("Gooblygook", true, false)]
|
||||
//[InlineData("--", false, false)]
|
||||
//public async Task CanTwoFactorAuthenticatorSignIn(string providerName, bool isPersistent, bool rememberClient)
|
||||
//{
|
||||
// // Setup
|
||||
// var user = new TestUser { UserName = "Foo" };
|
||||
// const string code = "3123";
|
||||
// var manager = SetupUserManager(user);
|
||||
// manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
// manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, providerName ?? TokenOptions.DefaultAuthenticatorProvider, code)).ReturnsAsync(true).Verifiable();
|
||||
// manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
// var context = new DefaultHttpContext();
|
||||
// var auth = MockAuth(context);
|
||||
// var helper = SetupSignInManager(manager.Object, context);
|
||||
// var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
|
||||
// if (providerName != null)
|
||||
// {
|
||||
// helper.Options.Tokens.AuthenticatorTokenProvider = providerName;
|
||||
// }
|
||||
// var id = helper.StoreTwoFactorInfo(user.Id, null);
|
||||
// SetupSignIn(context, auth, user.Id, isPersistent);
|
||||
// auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
|
||||
// .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
|
||||
// if (rememberClient)
|
||||
// {
|
||||
// auth.Setup(a => a.SignInAsync(context,
|
||||
// IdentityConstants.TwoFactorRememberMeScheme,
|
||||
// It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
|
||||
// && i.Identities.First().AuthenticationType == IdentityConstants.TwoFactorRememberMeScheme),
|
||||
// It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// }
|
||||
|
||||
// // Act
|
||||
// var result = await helper.TwoFactorAuthenticatorSignInAsync(code, isPersistent, rememberClient);
|
||||
|
||||
// // Assert
|
||||
// Assert.True(result.Succeeded);
|
||||
// manager.Verify();
|
||||
// auth.Verify();
|
||||
//}
|
||||
|
||||
//[Theory]
|
||||
//[InlineData(true, true)]
|
||||
//[InlineData(true, false)]
|
||||
//[InlineData(false, true)]
|
||||
//[InlineData(false, false)]
|
||||
//public async Task CanTwoFactorRecoveryCodeSignIn(bool supportsLockout, bool externalLogin)
|
||||
//{
|
||||
// // Setup
|
||||
// var user = new TestUser { UserName = "Foo" };
|
||||
// const string bypassCode = "someCode";
|
||||
// var manager = SetupUserManager(user);
|
||||
// manager.Setup(m => m.SupportsUserLockout).Returns(supportsLockout).Verifiable();
|
||||
// manager.Setup(m => m.RedeemTwoFactorRecoveryCodeAsync(user, bypassCode)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
// if (supportsLockout)
|
||||
// {
|
||||
// manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
// }
|
||||
// var context = new DefaultHttpContext();
|
||||
// var auth = MockAuth(context);
|
||||
// var helper = SetupSignInManager(manager.Object, context);
|
||||
// var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
|
||||
// var loginProvider = "loginprovider";
|
||||
// var id = helper.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
|
||||
// if (externalLogin)
|
||||
// {
|
||||
// auth.Setup(a => a.SignInAsync(context,
|
||||
// IdentityConstants.ApplicationScheme,
|
||||
// It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider
|
||||
// && i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id),
|
||||
// It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ExternalScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// auth.Setup(a => a.SignOutAsync(context, IdentityConstants.TwoFactorUserIdScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// SetupSignIn(context, auth, user.Id);
|
||||
// }
|
||||
// auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
|
||||
// .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
|
||||
|
||||
// // Act
|
||||
// var result = await helper.TwoFactorRecoveryCodeSignInAsync(bypassCode);
|
||||
|
||||
// // Assert
|
||||
// Assert.True(result.Succeeded);
|
||||
// manager.Verify();
|
||||
// auth.Verify();
|
||||
//}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public async Task CanExternalSignIn(bool isPersistent, bool supportsLockout)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
const string loginProvider = "login";
|
||||
const string providerKey = "fookey";
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(supportsLockout).Verifiable();
|
||||
if (supportsLockout)
|
||||
{
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
}
|
||||
manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
SetupSignIn(context, auth, user.Id, isPersistent, loginProvider);
|
||||
|
||||
// Act
|
||||
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CanResignIn(bool externalLogin)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
var loginProvider = "loginprovider";
|
||||
var id = new ClaimsIdentity();
|
||||
if (externalLogin)
|
||||
{
|
||||
id.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, loginProvider));
|
||||
}
|
||||
// REVIEW: auth changes we lost the ability to mock is persistent
|
||||
//var properties = new AuthenticationProperties { IsPersistent = isPersistent };
|
||||
var authResult = AuthenticateResult.NoResult();
|
||||
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme))
|
||||
.Returns(Task.FromResult(authResult)).Verifiable();
|
||||
var manager = SetupUserManager(user);
|
||||
var signInManager = new Mock<SignInManager<TestUser>>(manager.Object,
|
||||
new HttpContextAccessor { HttpContext = context },
|
||||
new Mock<IUserClaimsPrincipalFactory<TestUser>>().Object,
|
||||
null, null, new Mock<IAuthenticationSchemeProvider>().Object)
|
||||
{ CallBase = true };
|
||||
//signInManager.Setup(s => s.SignInAsync(user, It.Is<AuthenticationProperties>(p => p.IsPersistent == isPersistent),
|
||||
//externalLogin? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable();
|
||||
signInManager.Setup(s => s.SignInAsync(user, It.IsAny<AuthenticationProperties>(), null)).Returns(Task.FromResult(0)).Verifiable();
|
||||
signInManager.Object.Context = context;
|
||||
|
||||
// Act
|
||||
await signInManager.Object.RefreshSignInAsync(user);
|
||||
|
||||
// Assert
|
||||
auth.Verify();
|
||||
signInManager.Verify();
|
||||
}
|
||||
|
||||
//[Theory]
|
||||
//[InlineData(true, true, true, true)]
|
||||
//[InlineData(true, true, false, true)]
|
||||
//[InlineData(true, false, true, true)]
|
||||
//[InlineData(true, false, false, true)]
|
||||
//[InlineData(false, true, true, true)]
|
||||
//[InlineData(false, true, false, true)]
|
||||
//[InlineData(false, false, true, true)]
|
||||
//[InlineData(false, false, false, true)]
|
||||
//[InlineData(true, true, true, false)]
|
||||
//[InlineData(true, true, false, false)]
|
||||
//[InlineData(true, false, true, false)]
|
||||
//[InlineData(true, false, false, false)]
|
||||
//[InlineData(false, true, true, false)]
|
||||
//[InlineData(false, true, false, false)]
|
||||
//[InlineData(false, false, true, false)]
|
||||
//[InlineData(false, false, false, false)]
|
||||
//public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bool externalLogin, bool rememberClient)
|
||||
//{
|
||||
// // Setup
|
||||
// var user = new TestUser { UserName = "Foo" };
|
||||
// var manager = SetupUserManager(user);
|
||||
// var provider = "twofactorprovider";
|
||||
// var code = "123456";
|
||||
// manager.Setup(m => m.SupportsUserLockout).Returns(supportsLockout).Verifiable();
|
||||
// if (supportsLockout)
|
||||
// {
|
||||
// manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
// manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
// }
|
||||
// manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(true).Verifiable();
|
||||
// var context = new DefaultHttpContext();
|
||||
// var auth = MockAuth(context);
|
||||
// var helper = SetupSignInManager(manager.Object, context);
|
||||
// var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
|
||||
// var loginProvider = "loginprovider";
|
||||
// var id = helper.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
|
||||
// if (externalLogin)
|
||||
// {
|
||||
// auth.Setup(a => a.SignInAsync(context,
|
||||
// IdentityConstants.ApplicationScheme,
|
||||
// It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider
|
||||
// && i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id),
|
||||
// It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// // REVIEW: restore ability to test is persistent
|
||||
// //It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
|
||||
// auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ExternalScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// auth.Setup(a => a.SignOutAsync(context, IdentityConstants.TwoFactorUserIdScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// SetupSignIn(context, auth, user.Id);
|
||||
// }
|
||||
// if (rememberClient)
|
||||
// {
|
||||
// auth.Setup(a => a.SignInAsync(context,
|
||||
// IdentityConstants.TwoFactorRememberMeScheme,
|
||||
// It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
|
||||
// && i.Identities.First().AuthenticationType == IdentityConstants.TwoFactorRememberMeScheme),
|
||||
// It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
// //It.Is<AuthenticationProperties>(v => v.IsPersistent == true))).Returns(Task.FromResult(0)).Verifiable();
|
||||
// }
|
||||
// auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
|
||||
// .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
|
||||
|
||||
// // Act
|
||||
// var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
|
||||
|
||||
// // Assert
|
||||
// Assert.True(result.Succeeded);
|
||||
// manager.Verify();
|
||||
// auth.Verify();
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public async Task RememberClientStoresUserId()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
auth.Setup(a => a.SignInAsync(
|
||||
context,
|
||||
IdentityConstants.TwoFactorRememberMeScheme,
|
||||
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
|
||||
&& i.Identities.First().AuthenticationType == IdentityConstants.TwoFactorRememberMeScheme),
|
||||
It.Is<AuthenticationProperties>(v => v.IsPersistent == true))).Returns(Task.FromResult(0)).Verifiable();
|
||||
|
||||
|
||||
// Act
|
||||
await helper.RememberTwoFactorClientAsync(user);
|
||||
|
||||
// Assert
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task RememberBrowserSkipsTwoFactorVerificationSignIn(bool isPersistent)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable();
|
||||
IList<string> providers = new List<string>();
|
||||
providers.Add("PhoneNumber");
|
||||
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).Returns(Task.FromResult(providers)).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
SetupSignIn(context, auth);
|
||||
var id = new ClaimsIdentity(IdentityConstants.TwoFactorRememberMeScheme);
|
||||
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
||||
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorRememberMeScheme))
|
||||
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), null, IdentityConstants.TwoFactorRememberMeScheme))).Verifiable();
|
||||
var helper = SetupSignInManager(manager.Object, context);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Succeeded);
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
private Mock<IAuthenticationService> MockAuth(HttpContext context)
|
||||
{
|
||||
var auth = new Mock<IAuthenticationService>();
|
||||
context.RequestServices = new ServiceCollection().AddSingleton(auth.Object).BuildServiceProvider();
|
||||
return auth;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutCallsContextResponseSignOut()
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.TestUserManager<TestUser>();
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ApplicationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
auth.Setup(a => a.SignOutAsync(context, IdentityConstants.TwoFactorUserIdScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ExternalScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
|
||||
var helper = SetupSignInManager(manager, context, null, manager.Options);
|
||||
|
||||
// Act
|
||||
await helper.SignOutAsync();
|
||||
|
||||
// Assert
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PasswordSignInFailsWithWrongPassword()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
|
||||
var context = new Mock<HttpContext>();
|
||||
var logStore = new StringBuilder();
|
||||
var helper = SetupSignInManager(manager.Object, context.Object, logStore);
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||
var checkResult = await helper.CheckPasswordSignInAsync(user, "bogus", false);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.False(checkResult.Succeeded);
|
||||
Assert.True(logStore.ToString().Contains($"User {user.Id} failed to provide the correct password."));
|
||||
manager.Verify();
|
||||
context.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PasswordSignInFailsWithUnknownUser()
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||
manager.Setup(m => m.FindByNameAsync("bogus")).ReturnsAsync(default(TestUser)).Verifiable();
|
||||
var context = new Mock<HttpContext>();
|
||||
var helper = SetupSignInManager(manager.Object, context.Object);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
manager.Verify();
|
||||
context.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PasswordSignInFailsWithWrongPasswordCanAccessFailedAndLockout()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
var lockedout = false;
|
||||
manager.Setup(m => m.AccessFailedAsync(user)).Returns(() =>
|
||||
{
|
||||
lockedout = true;
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
|
||||
var context = new Mock<HttpContext>();
|
||||
var helper = SetupSignInManager(manager.Object, context.Object);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.True(result.IsLockedOut);
|
||||
manager.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckPasswordSignInFailsWithWrongPasswordCanAccessFailedAndLockout()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
var lockedout = false;
|
||||
manager.Setup(m => m.AccessFailedAsync(user)).Returns(() =>
|
||||
{
|
||||
lockedout = true;
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
|
||||
var context = new Mock<HttpContext>();
|
||||
var helper = SetupSignInManager(manager.Object, context.Object);
|
||||
|
||||
// Act
|
||||
var result = await helper.CheckPasswordSignInAsync(user, "bogus", true);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.True(result.IsLockedOut);
|
||||
manager.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CanRequireConfirmedEmailForPasswordSignIn(bool confirmed)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.IsEmailConfirmedAsync(user)).ReturnsAsync(confirmed).Verifiable();
|
||||
if (confirmed)
|
||||
{
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
}
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
if (confirmed)
|
||||
{
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
SetupSignIn(context, auth);
|
||||
}
|
||||
var identityOptions = new IdentityOptions();
|
||||
identityOptions.SignIn.RequireConfirmedEmail = true;
|
||||
var logStore = new StringBuilder();
|
||||
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
||||
|
||||
// Assert
|
||||
|
||||
Assert.Equal(confirmed, result.Succeeded);
|
||||
Assert.NotEqual(confirmed, result.IsNotAllowed);
|
||||
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email."));
|
||||
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
|
||||
private static void SetupSignIn(HttpContext context, Mock<IAuthenticationService> auth, string userId = null, bool? isPersistent = null, string loginProvider = null)
|
||||
{
|
||||
auth.Setup(a => a.SignInAsync(context,
|
||||
IdentityConstants.ApplicationScheme,
|
||||
It.Is<ClaimsPrincipal>(id =>
|
||||
(userId == null || id.FindFirstValue(ClaimTypes.NameIdentifier) == userId) &&
|
||||
(loginProvider == null || id.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider)),
|
||||
It.Is<AuthenticationProperties>(v => isPersistent == null || v.IsPersistent == isPersistent))).Returns(Task.FromResult(0)).Verifiable();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CanRequireConfirmedPhoneNumberForPasswordSignIn(bool confirmed)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.IsPhoneNumberConfirmedAsync(user)).ReturnsAsync(confirmed).Verifiable();
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
if (confirmed)
|
||||
{
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
SetupSignIn(context, auth);
|
||||
}
|
||||
|
||||
var identityOptions = new IdentityOptions();
|
||||
identityOptions.SignIn.RequireConfirmedPhoneNumber = true;
|
||||
var logStore = new StringBuilder();
|
||||
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions);
|
||||
|
||||
// Act
|
||||
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(confirmed, result.Succeeded);
|
||||
Assert.NotEqual(confirmed, result.IsNotAllowed);
|
||||
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number."));
|
||||
manager.Verify();
|
||||
auth.Verify();
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -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;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class UserClaimsPrincipalFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateIdentityNullChecks()
|
||||
{
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>().Object;
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>().Object;
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
Assert.Throws<ArgumentNullException>("optionsAccessor",
|
||||
() => new UserClaimsPrincipalFactory<TestUser, TestRole>(userManager, roleManager, options.Object));
|
||||
var identityOptions = new IdentityOptions();
|
||||
options.Setup(a => a.Value).Returns(identityOptions);
|
||||
var factory = new UserClaimsPrincipalFactory<TestUser, TestRole>(userManager, roleManager, options.Object);
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await factory.CreateAsync(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, false)]
|
||||
[InlineData(false, true, false)]
|
||||
[InlineData(true, false, false)]
|
||||
[InlineData(true, true, false)]
|
||||
[InlineData(true, false, true)]
|
||||
[InlineData(true, true, true)]
|
||||
public async Task EnsureClaimsIdentityHasExpectedClaims(bool supportRoles, bool supportClaims, bool supportRoleClaims)
|
||||
{
|
||||
// Setup
|
||||
var userManager = MockHelpers.MockUserManager<TestUser>();
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
userManager.Setup(m => m.SupportsUserClaim).Returns(supportClaims);
|
||||
userManager.Setup(m => m.SupportsUserRole).Returns(supportRoles);
|
||||
userManager.Setup(m => m.GetUserIdAsync(user)).ReturnsAsync(user.Id);
|
||||
userManager.Setup(m => m.GetUserNameAsync(user)).ReturnsAsync(user.UserName);
|
||||
var roleClaims = new[] { "Admin", "Local" };
|
||||
if (supportRoles)
|
||||
{
|
||||
userManager.Setup(m => m.GetRolesAsync(user)).ReturnsAsync(roleClaims);
|
||||
roleManager.Setup(m => m.SupportsRoleClaims).Returns(supportRoleClaims);
|
||||
}
|
||||
var userClaims = new[] { new Claim("Whatever", "Value"), new Claim("Whatever2", "Value2") };
|
||||
if (supportClaims)
|
||||
{
|
||||
userManager.Setup(m => m.GetClaimsAsync(user)).ReturnsAsync(userClaims);
|
||||
}
|
||||
userManager.Object.Options = new IdentityOptions();
|
||||
|
||||
var admin = new TestRole() { Name = "Admin" };
|
||||
var local = new TestRole() { Name = "Local" };
|
||||
var adminClaims = new[] { new Claim("AdminClaim1", "Value1"), new Claim("AdminClaim2", "Value2") };
|
||||
var localClaims = new[] { new Claim("LocalClaim1", "Value1"), new Claim("LocalClaim2", "Value2") };
|
||||
if (supportRoleClaims)
|
||||
{
|
||||
roleManager.Setup(m => m.FindByNameAsync("Admin")).ReturnsAsync(admin);
|
||||
roleManager.Setup(m => m.FindByNameAsync("Local")).ReturnsAsync(local);
|
||||
roleManager.Setup(m => m.GetClaimsAsync(admin)).ReturnsAsync(adminClaims);
|
||||
roleManager.Setup(m => m.GetClaimsAsync(local)).ReturnsAsync(localClaims);
|
||||
}
|
||||
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
options.Setup(a => a.Value).Returns(identityOptions);
|
||||
var factory = new UserClaimsPrincipalFactory<TestUser, TestRole>(userManager.Object, roleManager.Object, options.Object);
|
||||
|
||||
// Act
|
||||
var principal = await factory.CreateAsync(user);
|
||||
var identity = principal.Identities.First();
|
||||
|
||||
// Assert
|
||||
var manager = userManager.Object;
|
||||
Assert.NotNull(identity);
|
||||
Assert.Single(principal.Identities);
|
||||
Assert.Equal(IdentityConstants.ApplicationScheme, identity.AuthenticationType);
|
||||
var claims = identity.Claims.ToList();
|
||||
Assert.NotNull(claims);
|
||||
Assert.Contains(claims, c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName);
|
||||
Assert.Contains(claims, c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id);
|
||||
Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin"));
|
||||
Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local"));
|
||||
foreach (var cl in userClaims)
|
||||
{
|
||||
Assert.Equal(supportClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
|
||||
}
|
||||
foreach (var cl in adminClaims)
|
||||
{
|
||||
Assert.Equal(supportRoleClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
|
||||
}
|
||||
foreach (var cl in localClaims)
|
||||
{
|
||||
Assert.Equal(supportRoleClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
|
||||
}
|
||||
userManager.VerifyAll();
|
||||
roleManager.VerifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1595
File diff suppressed because it is too large
Load Diff
+99
@@ -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.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public class UserValidatorTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ValidateThrowsWithNull()
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
var validator = new UserValidator<TestUser>();
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", () => validator.ValidateAsync(manager, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public async Task ValidateFailsWithTooShortUserNames(string input)
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
var validator = new UserValidator<TestUser>();
|
||||
var user = new TestUser {UserName = input};
|
||||
|
||||
// Act
|
||||
var result = await validator.ValidateAsync(manager, user);
|
||||
|
||||
// Assert
|
||||
IdentityResultAssert.IsFailure(result, new IdentityErrorDescriber().InvalidUserName(input));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test_email@foo.com", true)]
|
||||
[InlineData("hao", true)]
|
||||
[InlineData("test123", true)]
|
||||
[InlineData("hyphen-yes@foo-bar.com", true)]
|
||||
[InlineData("+plus+yes+@foo-bar.com", true)]
|
||||
[InlineData("!noway", false)]
|
||||
[InlineData("foo@boz#.com", false)]
|
||||
public async Task DefaultAlphaNumericOnlyUserNameValidation(string userName, bool expectSuccess)
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
var validator = new UserValidator<TestUser>();
|
||||
var user = new TestUser {UserName = userName};
|
||||
|
||||
// Act
|
||||
var result = await validator.ValidateAsync(manager, user);
|
||||
|
||||
// Assert
|
||||
if (expectSuccess)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdentityResultAssert.IsFailure(result);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("test_email@foo.com", true)]
|
||||
[InlineData("hao", true)]
|
||||
[InlineData("test123", true)]
|
||||
[InlineData("!noway", true)]
|
||||
[InlineData("foo@boz#.com", true)]
|
||||
public async Task CanAllowNonAlphaNumericUserName(string userName, bool expectSuccess)
|
||||
{
|
||||
// Setup
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
manager.Options.User.AllowedUserNameCharacters = null;
|
||||
var validator = new UserValidator<TestUser>();
|
||||
var user = new TestUser {UserName = userName};
|
||||
|
||||
// Act
|
||||
var result = await validator.ValidateAsync(manager, user);
|
||||
|
||||
// Assert
|
||||
if (expectSuccess)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
IdentityResultAssert.IsFailure(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Extensions;
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure
|
||||
{
|
||||
public static class Locks
|
||||
{
|
||||
public static object MongoInitLock = new object();
|
||||
public static object RolesLock = new object();
|
||||
}
|
||||
|
||||
public static class Container
|
||||
{
|
||||
public static MongoDbIdentityConfiguration MongoDbIdentityConfiguration = new MongoDbIdentityConfiguration
|
||||
{
|
||||
MongoDbSettings = new MongoDbSettings
|
||||
{
|
||||
ConnectionString = "mongodb://localhost:27017",
|
||||
DatabaseName = "MongoDbTests"
|
||||
},
|
||||
IdentityOptionsAction = options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.User.AllowedUserNameCharacters = null;
|
||||
}
|
||||
};
|
||||
|
||||
public static IServiceProvider Instance { get; set; }
|
||||
|
||||
const string connectionString = "mongodb://localhost:27017";
|
||||
private static readonly IMongoRepository _mongoDbRepository;
|
||||
|
||||
private static readonly IMongoRepository _mongoDbRepository2;
|
||||
|
||||
static Container()
|
||||
{
|
||||
lock (Locks.MongoInitLock)
|
||||
{
|
||||
_mongoDbRepository = new MongoRepository(
|
||||
MongoDbIdentityConfiguration.MongoDbSettings.ConnectionString,
|
||||
MongoDbIdentityConfiguration.MongoDbSettings.DatabaseName);
|
||||
_mongoDbRepository2 = new MongoRepository(
|
||||
MongoDbIdentityConfiguration.MongoDbSettings.ConnectionString,
|
||||
MongoDbIdentityConfiguration.MongoDbSettings.DatabaseName);
|
||||
}
|
||||
}
|
||||
|
||||
public static IMongoRepository MongoRepository
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mongoDbRepository;
|
||||
}
|
||||
}
|
||||
|
||||
public static IMongoRepository MongoRepositoryConcurrent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mongoDbRepository2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure
|
||||
{
|
||||
public class TestMongoIdentityRole : MongoIdentityRole<Guid>
|
||||
{
|
||||
public TestMongoIdentityRole() : base ()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public TestMongoIdentityRole(string roleName) : base(roleName)
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using System;
|
||||
|
||||
namespace AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure
|
||||
{
|
||||
public class TestMongoIdentityUser : MongoIdentityUser<Guid>
|
||||
{
|
||||
public TestMongoIdentityUser() : base()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public TestMongoIdentityUser(string userName) : base(userName)
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public TestMongoIdentityUser(string userName, string email) : base(userName, email)
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public string CustomContent { get; set; }
|
||||
}
|
||||
}
|
||||
+97
@@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public abstract class ApiConsistencyTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Public_inheritable_apis_should_be_virtual()
|
||||
{
|
||||
var nonVirtualMethods
|
||||
= (from type in GetAllTypes(TargetAssembly.DefinedTypes)
|
||||
where type.IsVisible
|
||||
&& !type.IsSealed
|
||||
&& type.DeclaredConstructors.Any(c => c.IsPublic || c.IsFamily || c.IsFamilyOrAssembly)
|
||||
&& type.Namespace != null
|
||||
&& !type.Namespace.EndsWith(".Compiled")
|
||||
from method in type.DeclaredMethods.Where(m => m.IsPublic && !m.IsStatic)
|
||||
where GetBasestTypeInAssembly(method.DeclaringType) == type
|
||||
&& !(method.IsVirtual && !method.IsFinal)
|
||||
&& !method.Name.StartsWith("get_")
|
||||
&& !method.Name.StartsWith("set_")
|
||||
&& !method.Name.Equals("Dispose")
|
||||
select type.Name + "." + method.Name)
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
nonVirtualMethods.Any(),
|
||||
"\r\n-- Missing virtual APIs --\r\n" + string.Join("\r\n", nonVirtualMethods));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Async_methods_should_end_with_async_suffix()
|
||||
{
|
||||
var asyncMethods
|
||||
= (from type in GetAllTypes(TargetAssembly.DefinedTypes)
|
||||
where type.IsVisible
|
||||
from method in type.DeclaredMethods.Where(m => m.IsPublic)
|
||||
where GetBasestTypeInAssembly(method.DeclaringType) == type
|
||||
where typeof(Task).IsAssignableFrom(method.ReturnType)
|
||||
select method).ToList();
|
||||
|
||||
var missingSuffixMethods
|
||||
= asyncMethods
|
||||
.Where(method => !method.Name.EndsWith("Async"))
|
||||
.Select(method => method.DeclaringType.Name + "." + method.Name)
|
||||
.Except(GetAsyncSuffixExceptions())
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
missingSuffixMethods.Any(),
|
||||
"\r\n-- Missing async suffix --\r\n" + string.Join("\r\n", missingSuffixMethods));
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<string> GetCancellationTokenExceptions()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<string> GetAsyncSuffixExceptions()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
protected abstract Assembly TargetAssembly { get; }
|
||||
|
||||
protected virtual IEnumerable<TypeInfo> GetAllTypes(IEnumerable<TypeInfo> types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
{
|
||||
yield return type;
|
||||
|
||||
foreach (var nestedType in GetAllTypes(type.DeclaredNestedTypes))
|
||||
{
|
||||
yield return nestedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected TypeInfo GetBasestTypeInAssembly(Type type)
|
||||
{
|
||||
while (type.GetTypeInfo()?.BaseType?.GetTypeInfo()?.Assembly == type.GetTypeInfo().Assembly)
|
||||
{
|
||||
type = type.GetTypeInfo().BaseType;
|
||||
}
|
||||
|
||||
return type.GetTypeInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
public static class MockHelpers
|
||||
{
|
||||
public static StringBuilder LogMessage = new StringBuilder();
|
||||
|
||||
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
|
||||
{
|
||||
var store = new Mock<IUserStore<TUser>>();
|
||||
var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null);
|
||||
mgr.Object.UserValidators.Add(new UserValidator<TUser>());
|
||||
mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());
|
||||
return mgr;
|
||||
}
|
||||
|
||||
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>(IRoleStore<TRole> store = null) where TRole : class
|
||||
{
|
||||
store = store ?? new Mock<IRoleStore<TRole>>().Object;
|
||||
var roles = new List<IRoleValidator<TRole>>();
|
||||
roles.Add(new RoleValidator<TRole>());
|
||||
return new Mock<RoleManager<TRole>>(store, roles, new UpperInvariantLookupNormalizer(),
|
||||
new IdentityErrorDescriber(), null);
|
||||
}
|
||||
|
||||
public static Mock<ILogger<T>> MockILogger<T>(StringBuilder logStore = null) where T : class
|
||||
{
|
||||
logStore = logStore ?? LogMessage;
|
||||
var logger = new Mock<ILogger<T>>();
|
||||
logger.Setup(x => x.Log(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<object>(),
|
||||
It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>()))
|
||||
.Callback((LogLevel logLevel, EventId eventId, object state, Exception exception, Func<object, Exception, string> formatter) =>
|
||||
{
|
||||
if (formatter == null)
|
||||
{
|
||||
logStore.Append(state.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
logStore.Append(formatter(state, exception));
|
||||
}
|
||||
logStore.Append(" ");
|
||||
});
|
||||
logger.Setup(x => x.BeginScope(It.IsAny<object>())).Callback((object state) =>
|
||||
{
|
||||
logStore.Append(state.ToString());
|
||||
logStore.Append(" ");
|
||||
});
|
||||
logger.Setup(x => x.IsEnabled(LogLevel.Debug)).Returns(true);
|
||||
logger.Setup(x => x.IsEnabled(LogLevel.Warning)).Returns(true);
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
|
||||
{
|
||||
store = store ?? new Mock<IUserStore<TUser>>().Object;
|
||||
var options = new Mock<IOptions<IdentityOptions>>();
|
||||
var idOptions = new IdentityOptions();
|
||||
idOptions.Lockout.AllowedForNewUsers = false;
|
||||
options.Setup(o => o.Value).Returns(idOptions);
|
||||
var userValidators = new List<IUserValidator<TUser>>();
|
||||
var validator = new Mock<IUserValidator<TUser>>();
|
||||
userValidators.Add(validator.Object);
|
||||
var pwdValidators = new List<PasswordValidator<TUser>>();
|
||||
pwdValidators.Add(new PasswordValidator<TUser>());
|
||||
var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
|
||||
userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
|
||||
new IdentityErrorDescriber(), null,
|
||||
new Mock<ILogger<UserManager<TUser>>>().Object);
|
||||
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>()))
|
||||
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
|
||||
return userManager;
|
||||
}
|
||||
|
||||
public static RoleManager<TRole> TestRoleManager<TRole>(IRoleStore<TRole> store = null) where TRole : class
|
||||
{
|
||||
store = store ?? new Mock<IRoleStore<TRole>>().Object;
|
||||
var roles = new List<IRoleValidator<TRole>>();
|
||||
roles.Add(new RoleValidator<TRole>());
|
||||
return new AspNetRoleManager<TRole>(store, roles,
|
||||
new UpperInvariantLookupNormalizer(),
|
||||
new IdentityErrorDescriber(),
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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.Test
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
/// <summary>
|
||||
/// Test priority
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class TestPriorityAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="priority"></param>
|
||||
public TestPriorityAttribute(int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority
|
||||
/// </summary>
|
||||
public int Priority { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to run tests in order.
|
||||
/// </summary>
|
||||
public class PriorityOrderer : ITestCaseOrderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Orders tests cases
|
||||
/// </summary>
|
||||
/// <typeparam name="XunitTestCase"></typeparam>
|
||||
/// <param name="testCases"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<XunitTestCase> OrderTestCases<XunitTestCase>(IEnumerable<XunitTestCase> testCases) where XunitTestCase : ITestCase
|
||||
{
|
||||
var sortedMethods = new SortedDictionary<int, List<XunitTestCase>>();
|
||||
|
||||
foreach (XunitTestCase testCase in testCases)
|
||||
{
|
||||
int priority = 0;
|
||||
|
||||
foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute)).AssemblyQualifiedName))
|
||||
priority = attr.GetNamedArgument<int>("Priority");
|
||||
|
||||
GetOrCreate(sortedMethods, priority).Add(testCase);
|
||||
}
|
||||
|
||||
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
|
||||
{
|
||||
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
|
||||
foreach (XunitTestCase testCase in list)
|
||||
yield return testCase;
|
||||
}
|
||||
}
|
||||
|
||||
static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key) where TValue : new()
|
||||
{
|
||||
TValue result;
|
||||
|
||||
if (dictionary.TryGetValue(key, out result)) return result;
|
||||
|
||||
result = new TValue();
|
||||
dictionary[key] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Role entity
|
||||
/// </summary>
|
||||
public class TestRole : TestRole<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public TestRole()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="roleName"></param>
|
||||
public TestRole(string roleName) : this()
|
||||
{
|
||||
Name = roleName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Role entity
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestRole<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public TestRole() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="roleName"></param>
|
||||
public TestRole(string roleName) : this()
|
||||
{
|
||||
Name = roleName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Role id
|
||||
/// </summary>
|
||||
public virtual TKey Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property for claims in the role
|
||||
/// </summary>
|
||||
public virtual ICollection<TestRoleClaim<TKey>> Claims { get; private set; } = new List<TestRoleClaim<TKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// Role name
|
||||
/// </summary>
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized name used for equality
|
||||
/// </summary>
|
||||
public virtual string NormalizedName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A random value that should change whenever a role is persisted to the store
|
||||
/// </summary>
|
||||
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityType that represents one specific role claim
|
||||
/// </summary>
|
||||
public class TestRoleClaim : TestRoleClaim<string> { }
|
||||
|
||||
/// <summary>
|
||||
/// EntityType that represents one specific role claim
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestRoleClaim<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary key
|
||||
/// </summary>
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User Id for the role this claim belongs to
|
||||
/// </summary>
|
||||
public virtual TKey RoleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Claim type
|
||||
/// </summary>
|
||||
public virtual string ClaimType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Claim value
|
||||
/// </summary>
|
||||
public virtual string ClaimValue { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Test user class
|
||||
/// </summary>
|
||||
public class TestUser : TestUser<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
public TestUser()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
public TestUser(string userName) : this()
|
||||
{
|
||||
UserName = userName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test user
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestUser<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
public TestUser() { }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
public TestUser(string userName) : this()
|
||||
{
|
||||
UserName = userName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public virtual TKey Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public virtual string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// normalized user name
|
||||
/// </summary>
|
||||
public virtual string NormalizedUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Email
|
||||
/// </summary>
|
||||
public virtual string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// normalized email
|
||||
/// </summary>
|
||||
public virtual string NormalizedEmail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the email is confirmed, default is false
|
||||
/// </summary>
|
||||
public virtual bool EmailConfirmed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The salted/hashed form of the user password
|
||||
/// </summary>
|
||||
public virtual string PasswordHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A random value that should change whenever a users credentials change (password changed, login removed)
|
||||
/// </summary>
|
||||
public virtual string SecurityStamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A random value that should change whenever a user is persisted to the store
|
||||
/// </summary>
|
||||
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// PhoneNumber for the user
|
||||
/// </summary>
|
||||
public virtual string PhoneNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the phone number is confirmed, default is false
|
||||
/// </summary>
|
||||
public virtual bool PhoneNumberConfirmed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is two factor enabled for the user
|
||||
/// </summary>
|
||||
public virtual bool TwoFactorEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// DateTime in UTC when lockout ends, any time in the past is considered not locked out.
|
||||
/// </summary>
|
||||
public virtual DateTimeOffset? LockoutEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is lockout enabled for this user
|
||||
/// </summary>
|
||||
public virtual bool LockoutEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to record failures for the purposes of lockout
|
||||
/// </summary>
|
||||
public virtual int AccessFailedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property
|
||||
/// </summary>
|
||||
public virtual ICollection<TestUserRole<TKey>> Roles { get; private set; } = new List<TestUserRole<TKey>>();
|
||||
/// <summary>
|
||||
/// Navigation property
|
||||
/// </summary>
|
||||
public virtual ICollection<TestUserClaim<TKey>> Claims { get; private set; } = new List<TestUserClaim<TKey>>();
|
||||
/// <summary>
|
||||
/// Navigation property
|
||||
/// </summary>
|
||||
public virtual ICollection<TestUserLogin<TKey>> Logins { get; private set; } = new List<TestUserLogin<TKey>>();
|
||||
/// <summary>
|
||||
/// Navigation property
|
||||
/// </summary>
|
||||
public virtual ICollection<TestUserToken<TKey>> Tokens { get; private set; } = new List<TestUserToken<TKey>>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityType that represents one specific user claim
|
||||
/// </summary>
|
||||
public class TestUserClaim : TestUserClaim<string> { }
|
||||
|
||||
/// <summary>
|
||||
/// EntityType that represents one specific user claim
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestUserClaim<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Primary key
|
||||
/// </summary>
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User Id for the user who owns this claim
|
||||
/// </summary>
|
||||
public virtual TKey UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Claim type
|
||||
/// </summary>
|
||||
public virtual string ClaimType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Claim value
|
||||
/// </summary>
|
||||
public virtual string ClaimValue { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity type for a user's login (i.e. facebook, google)
|
||||
/// </summary>
|
||||
public class TestUserLogin : TestUserLogin<string> { }
|
||||
|
||||
/// <summary>
|
||||
/// Entity type for a user's login (i.e. facebook, google)
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestUserLogin<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// The login provider for the login (i.e. facebook, google)
|
||||
/// </summary>
|
||||
public virtual string LoginProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key representing the login for the provider
|
||||
/// </summary>
|
||||
public virtual string ProviderKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the login
|
||||
/// </summary>
|
||||
public virtual string ProviderDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User Id for the user who owns this login
|
||||
/// </summary>
|
||||
public virtual TKey UserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityType that represents a user belonging to a role
|
||||
/// </summary>
|
||||
public class TestUserRole : TestUserRole<string> { }
|
||||
|
||||
/// <summary>
|
||||
/// EntityType that represents a user belonging to a role
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestUserRole<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// UserId for the user that is in the role
|
||||
/// </summary>
|
||||
public virtual TKey UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// RoleId for the role
|
||||
/// </summary>
|
||||
public virtual TKey RoleId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity type for a user's token
|
||||
/// </summary>
|
||||
public class TestUserToken : TestUserToken<string> { }
|
||||
|
||||
/// <summary>
|
||||
/// Entity type for a user's token
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TestUserToken<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// The login provider for the login (i.e. facebook, google)
|
||||
/// </summary>
|
||||
public virtual string LoginProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key representing the login for the provider
|
||||
/// </summary>
|
||||
public virtual string TokenName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the login
|
||||
/// </summary>
|
||||
public virtual string TokenValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User Id for the user who owns this login
|
||||
/// </summary>
|
||||
public virtual TKey UserId { get; set; }
|
||||
}
|
||||
}
|
||||
+73
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper for tests to validate identity results.
|
||||
/// </summary>
|
||||
public static class IdentityResultAssert
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts that the result has Succeeded.
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
public static void IsSuccess(IdentityResult result)
|
||||
{
|
||||
Assert.NotNull(result);
|
||||
Assert.True(result.Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the result has not Succeeded.
|
||||
/// </summary>
|
||||
public static void IsFailure(IdentityResult result)
|
||||
{
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the result has not Succeeded and that error is the first Error's Description.
|
||||
/// </summary>
|
||||
public static void IsFailure(IdentityResult result, string error)
|
||||
{
|
||||
Assert.NotNull(result);
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.Equal(error, result.Errors.First().Description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the result has not Succeeded and that first error matches error's code and Description.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that the logger contains the expectedLog.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger to inspect.</param>
|
||||
/// <param name="expectedLog">The expected log message.</param>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+717
@@ -0,0 +1,717 @@
|
||||
// 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;
|
||||
using AspNetCore.Identity.MongoDbCore.Models;
|
||||
using AspNetCore.Identity.MongoDbCore.Extensions;
|
||||
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
||||
using AspNetCore.Identity.MongoDbCore.IntegrationTests.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Common functionality tests that all verifies user manager functionality regardless of store implementation
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type of the user.</typeparam>
|
||||
/// <typeparam name="TRole">The type of the role.</typeparam>
|
||||
public abstract class IdentitySpecificationTestBase<TUser, TRole> : IdentitySpecificationTestBase<TUser, TRole, string>
|
||||
where TUser : MongoDbIdentityUser, new()
|
||||
where TRole : MongoDbIdentityRole, new()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Base class for tests that exercise basic identity functionality that all stores should support.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type of the user.</typeparam>
|
||||
/// <typeparam name="TRole">The type of the role.</typeparam>
|
||||
/// <typeparam name="TKey">The primary key type.</typeparam>
|
||||
public abstract class IdentitySpecificationTestBase<TUser, TRole, TKey> : UserManagerSpecificationTestBase<TUser, TKey>
|
||||
where TUser : MongoIdentityUser<TKey>, new()
|
||||
where TRole : MongoIdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Configure the service collection used for tests.
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="mongoRepository"></param>
|
||||
protected override void SetupIdentityServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.ConfigureMongoDbIdentity<TUser, TRole, TKey>(Container.MongoDbIdentityConfiguration, Container.MongoRepository.Context);
|
||||
services.AddLogging();
|
||||
services.AddSingleton<ILogger<UserManager<TUser>>>(new TestLogger<UserManager<TUser>>());
|
||||
services.AddSingleton<ILogger<RoleManager<TRole>>>(new TestLogger<RoleManager<TRole>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the service collection used for tests.
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="mongoRepository"></param>
|
||||
protected void SetupIdentityServices(IServiceCollection services, bool concurrentSetup = false)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
if (concurrentSetup)
|
||||
{
|
||||
services.ConfigureMongoDbIdentity<TUser, TRole, TKey>(Container.MongoDbIdentityConfiguration, Container.MongoRepositoryConcurrent.Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.ConfigureMongoDbIdentity<TUser, TRole, TKey>(Container.MongoDbIdentityConfiguration, Container.MongoRepository.Context);
|
||||
}
|
||||
|
||||
services.AddLogging();
|
||||
services.AddSingleton<ILogger<UserManager<TUser>>>(new TestLogger<UserManager<TUser>>());
|
||||
services.AddSingleton<ILogger<RoleManager<TRole>>>(new TestLogger<RoleManager<TRole>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup the IdentityBuilder
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected override IServiceCollection SetupBuilder(IServiceCollection services)
|
||||
{
|
||||
var builder = base.SetupBuilder(services);
|
||||
//builder.AddRoles<TRole>();
|
||||
//AddRoleStore(services, context);
|
||||
services.AddSingleton<ILogger<RoleManager<TRole>>>(new TestLogger<RoleManager<TRole>>());
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the role manager for tests.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that will be passed into the store, typically a db context.</param>
|
||||
/// <param name="services">The service collection to use, optional.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual RoleManager<TRole> CreateRoleManager(object context = null, IServiceCollection services = null)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
services = new ServiceCollection();
|
||||
}
|
||||
|
||||
if(context == null)
|
||||
{
|
||||
SetupIdentityServices(services);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupIdentityServices(services, true);
|
||||
}
|
||||
|
||||
return services.BuildServiceProvider().GetService<RoleManager<TRole>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an IRoleStore to services for the test.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to add to.</param>
|
||||
/// <param name="context">The context for the store to use, optional.</param>
|
||||
protected abstract void AddRoleStore(IServiceCollection services, object context = null);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new test role instance.
|
||||
/// </summary>
|
||||
/// <param name="roleNamePrefix">Optional name prefix, name will be randomized.</param>
|
||||
/// <param name="useRoleNamePrefixAsRoleName">If true, the prefix should be used as the rolename without a random pad.</param>
|
||||
/// <returns></returns>
|
||||
protected abstract TRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false);
|
||||
|
||||
/// <summary>
|
||||
/// Query used to do name equality checks.
|
||||
/// </summary>
|
||||
/// <param name="roleName">The role name to match.</param>
|
||||
/// <returns>The query to use.</returns>
|
||||
protected abstract Expression<Func<TRole, bool>> RoleNameEqualsPredicate(string roleName);
|
||||
|
||||
/// <summary>
|
||||
/// Query used to do user name prefix matching.
|
||||
/// </summary>
|
||||
/// <param name="roleName">The role name to match.</param>
|
||||
/// <returns>The query to use.</returns>
|
||||
protected abstract Expression<Func<TRole, bool>> RoleNameStartsWithPredicate(string roleName);
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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<TUser>, IRoleValidator<TRole>,
|
||||
IPasswordValidator<TUser>
|
||||
{
|
||||
public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad.", Code = "BadValidator" };
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
||||
}
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(RoleManager<TRole> manager, TRole role)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
||||
}
|
||||
|
||||
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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.Id, (await manager.FindByIdAsync(await manager.GetRoleIdAsync(role))).Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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.Id, (await manager.FindByNameAsync(roleName)).Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanUpdateRoleName()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateRoleManager();
|
||||
var roleName = "update" + Guid.NewGuid().ToString();
|
||||
var changedRoleName = "Changed" + 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, changedRoleName));
|
||||
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role));
|
||||
Assert.False(await manager.RoleExistsAsync(roleName));
|
||||
Assert.Equal(role.Id, (await manager.FindByNameAsync(changedRoleName)).Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanQueryableRoles()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateRoleManager();
|
||||
if (manager.SupportsQueryableRoles)
|
||||
{
|
||||
var guidName = "CanQuerableRolesTest" + Guid.NewGuid().ToString("n");
|
||||
var roles = GenerateRoles(guidName, 4);
|
||||
foreach (var r in roles)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(r));
|
||||
}
|
||||
Expression<Func<TRole, bool>> func = RoleNameStartsWithPredicate(guidName);
|
||||
Assert.Equal(roles.Count, manager.Roles.Count(func));
|
||||
func = RoleNameEqualsPredicate("bogus");
|
||||
Assert.Null(manager.Roles.FirstOrDefault(func));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanAddUsersToRole()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateManager();
|
||||
var roleManager = CreateRoleManager();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanGetRolesForUser()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var userManager = CreateManager();
|
||||
var roleManager = CreateRoleManager();
|
||||
var guid = Guid.NewGuid().ToString("n");
|
||||
var users = GenerateUsers(guid + "CanGetRolesForUser", 4);
|
||||
var roles = GenerateRoles(guid + "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.True(rs.Any(role => role == expectedRoleName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task RemoveUserFromRoleWithMultipleRoles()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userManager = CreateManager();
|
||||
var roleManager = CreateRoleManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user));
|
||||
var guidName = "RemoveUserFromRoleWithMultipleRoles" + Guid.NewGuid().ToString("n");
|
||||
var roles = GenerateRoles(guidName, 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])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanRemoveUsersFromRole()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userManager = CreateManager();
|
||||
var roleManager = CreateRoleManager();
|
||||
var guid = Guid.NewGuid().ToString("n");
|
||||
var users = GenerateUsers(guid + "CanRemoveUsersFromRole", 4);
|
||||
foreach (var u in users)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u));
|
||||
}
|
||||
var r = CreateTestRole(guid + "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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task RemoveUserNotInRoleFails()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userMgr = CreateManager();
|
||||
var roleMgr = CreateRoleManager();
|
||||
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}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task AddUserToRoleFailsIfAlreadyInRole()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userMgr = CreateManager();
|
||||
var roleMgr = CreateRoleManager();
|
||||
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}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task AddUserToRolesIgnoresDuplicates()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userMgr = CreateManager();
|
||||
var roleMgr = CreateRoleManager();
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanGetUsersInRole()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateManager();
|
||||
var roleManager = CreateRoleManager();
|
||||
var guidName = Guid.NewGuid().ToString("n") + "UsersInRole";
|
||||
var roles = GenerateRoles(guidName, 4);
|
||||
var roleNameList = new List<string>();
|
||||
|
||||
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<TRole> GenerateRoles(string namePrefix, int count)
|
||||
{
|
||||
var roles = new List<TRole>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
roles.Add(CreateTestRole(namePrefix + i));
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// test logger.
|
||||
/// </summary>
|
||||
public interface ITestLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// log messages.
|
||||
/// </summary>
|
||||
IList<string> LogMessages { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test logger.
|
||||
/// </summary>
|
||||
/// <typeparam name="TName"></typeparam>
|
||||
public class TestLogger<TName> : ILogger<TName>, ITestLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// log messages.
|
||||
/// </summary>
|
||||
public IList<string> LogMessages { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <typeparam name="TState"></typeparam>
|
||||
/// <param name="state"></param>
|
||||
/// <returns></returns>
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
LogMessages.Add(state?.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logLevel"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TState"></typeparam>
|
||||
/// <param name="logLevel"></param>
|
||||
/// <param name="eventId"></param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="exception"></param>
|
||||
/// <param name="formatter"></param>
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (formatter == null)
|
||||
{
|
||||
LogMessages.Add(state.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessages.Add(formatter(state, exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2116
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user