Merge pull request #23 from d-barker/master

Updated library to netcoreapp3.1 and netstandard2.1 (all unit tests passing)
This commit is contained in:
Alexandre SPIESER
2020-01-23 22:53:33 +00:00
committed by GitHub
10 changed files with 477 additions and 195 deletions
@@ -1,27 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
<UserSecretsId>aspnet-MongoIdentitySample.Mvc-95B15D82-54F6-4001-B4B0-6ADF4B1BB00E</UserSecretsId> <UserSecretsId>aspnet-MongoIdentitySample.Mvc-95B15D82-54F6-4001-B4B0-6ADF4B1BB00E</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCore.Identity.MongoDbCore" Version="1.1.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.13.0-beta1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" PrivateAssets="All" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.2.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.2.0" />
</ItemGroup> </ItemGroup>
@@ -31,4 +29,14 @@
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\AspNetCore.Identity.MongoDbCore.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>
+16 -10
View File
@@ -53,10 +53,10 @@ namespace MongoIdentitySample.Mvc
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env) //, ILoggerFactory loggerFactory)
{ {
loggerFactory.AddConsole(Configuration.GetSection("Logging")); //loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); //loggerFactory.AddDebug();
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
@@ -68,17 +68,23 @@ namespace MongoIdentitySample.Mvc
app.UseExceptionHandler("/Home/Error"); app.UseExceptionHandler("/Home/Error");
} }
app.UseRouting();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseIdentity(); app.UseAuthentication();
app.UseAuthorization();
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715 // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes => //app.UseMvc(routes =>
//{
// routes.MapRoute(
// name: "default",
// template: "{controller=Home}/{action=Index}/{id?}");
//});
app.UseEndpoints(endpoints =>
{ {
routes.MapRoute( endpoints.MapDefaultControllerRoute();
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); });
} }
} }
+6 -6
View File
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks> <TargetFrameworks>netcoreapp3.1;netstandard2.1</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\netstandard2.0\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile> <DocumentationFile>bin\Release\netstandard2.1\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\Debug\netstandard2.0\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile> <DocumentationFile>bin\Debug\netstandard2.1\AspNetCore.Identity.MongoDbCore.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
<PackageReference Include="MongoDB.Driver" Version="2.7.0" /> <PackageReference Include="MongoDB.Driver" Version="2.10.1" />
<PackageReference Include="MongoDbGenericRepository" Version="1.4.0" /> <PackageReference Include="MongoDbGenericRepository" Version="1.4.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+3
View File
@@ -211,10 +211,13 @@ namespace AspNetCore.Identity.MongoDbCore
var updateRes = await collection.ReplaceOneAsync(x => x.Id.Equals(user.Id) var updateRes = await collection.ReplaceOneAsync(x => x.Id.Equals(user.Id)
&& x.ConcurrencyStamp.Equals(oldStamp), && x.ConcurrencyStamp.Equals(oldStamp),
user); user);
if (updateRes.ModifiedCount == 0) if (updateRes.ModifiedCount == 0)
{ {
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
} }
return IdentityResult.Success; return IdentityResult.Success;
} }
@@ -1,30 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCore.Identity.MongoDbCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Http" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="MongoDB.Driver" Version="2.7.0" /> <PackageReference Include="MongoDB.Driver" Version="2.10.1" />
<PackageReference Include="MongoDbGenericRepository" Version="1.4.0" /> <PackageReference Include="MongoDbGenericRepository" Version="1.4.1" />
<PackageReference Include="Moq" Version="4.8.1" /> <PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\AspNetCore.Identity.MongoDbCore.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.Identity.Test
private class MySignInManager : SignInManager<TestUser> private class MySignInManager : SignInManager<TestUser>
{ {
public MySignInManager(UserManager<TestUser> manager, IHttpContextAccessor context, IUserClaimsPrincipalFactory<TestUser> claimsFactory) : base(manager, context, claimsFactory, null, null, null) { } public MySignInManager(UserManager<TestUser> manager, IHttpContextAccessor context, IUserClaimsPrincipalFactory<TestUser> claimsFactory) : base(manager, context, claimsFactory, null, null, null, null) { }
} }
private class MyUserManager : UserManager<TestUser> private class MyUserManager : UserManager<TestUser>
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Text;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
@@ -9,9 +10,11 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
using AspNetCore.Identity.MongoDbCore.IntegrationTests;
namespace Microsoft.AspNetCore.Identity.Test namespace Microsoft.AspNetCore.Identity.Test
{ {
@@ -71,150 +74,263 @@ namespace Microsoft.AspNetCore.Identity.Test
[InlineData(false)] [InlineData(false)]
public async Task OnValidatePrincipalTestSuccess(bool isPersistent) public async Task OnValidatePrincipalTestSuccess(bool isPersistent)
{ {
var user = new TestUser("test"); var user = new TestUser { UserName = "test" };
var userManager = MockHelpers.MockUserManager<TestUser>();
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>(); var manager = SetupUserManager(user);
var identityOptions = new Mock<IOptions<IdentityOptions>>(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); var context = new Mock<HttpContext>();
var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(new IdentityOptions());
var securityStampOptions = new Mock<IOptions<SecurityStampValidatorOptions>>();
securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var loggerFactory = new MockLoggerFactory();
var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new Mock<SignInManager<TestUser>>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1), IsPersistent = isPersistent };
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var principal = new ClaimsPrincipal(id); var principal = new ClaimsPrincipal(id);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1), IsPersistent = isPersistent }; helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable();
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object, helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable(); var logFactory = new MockLoggerFactory();
signInManager.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.AddSingleton(options.Object); services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object); services.AddSingleton(helper.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock())); services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory));
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var ticket = new AuthenticationTicket(principal, var ticket = new AuthenticationTicket(principal,
properties, properties,
IdentityConstants.ApplicationScheme); IdentityConstants.ApplicationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Properties); Assert.NotNull(cookieContext.Properties);
Assert.NotNull(context.Options); Assert.NotNull(cookieContext.Options);
Assert.NotNull(context.Principal); Assert.NotNull(cookieContext.Principal);
await await
SecurityStampValidator.ValidatePrincipalAsync(context); SecurityStampValidator.ValidatePrincipalAsync(cookieContext);
Assert.NotNull(context.Principal); Assert.NotNull(cookieContext.Principal);
signInManager.VerifyAll(); helper.VerifyAll();
}
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;
} }
[Fact] [Fact]
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails() public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
{ {
var user = new TestUser("test"); var user = new TestUser { UserName = "test" };
var userManager = MockHelpers.MockUserManager<TestUser>();
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>(); var manager = SetupUserManager(user);
var identityOptions = new Mock<IOptions<IdentityOptions>>(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
var options = new Mock<IOptions<SecurityStampValidatorOptions>>();
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); var context = new Mock<HttpContext>();
var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object); var roleManager = MockHelpers.MockRoleManager<TestRole>();
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
var services = new ServiceCollection(); var options = new Mock<IOptions<IdentityOptions>>();
services.AddSingleton(options.Object); options.Setup(a => a.Value).Returns(new IdentityOptions());
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock())); var securityStampOptions = new Mock<IOptions<SecurityStampValidatorOptions>>();
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = new MockLoggerFactory();
var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new Mock<SignInManager<TestUser>>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) };
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var principal = new ClaimsPrincipal(id);
//because the request fails the create user principal is never called and therefore not verifiable
//helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
//helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable();
helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(helper.Object);
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory));
context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) }, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) },
IdentityConstants.ApplicationScheme); IdentityConstants.ApplicationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Properties); var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal); Assert.NotNull(cookieContext.Properties);
await SecurityStampValidator.ValidatePrincipalAsync(context); Assert.NotNull(cookieContext.Options);
Assert.Null(context.Principal); Assert.NotNull(cookieContext.Principal);
signInManager.VerifyAll();
await SecurityStampValidator.ValidatePrincipalAsync(cookieContext);
Assert.Null(cookieContext.Principal);
helper.VerifyAll();
} }
[Fact] [Fact]
public async Task OnValidateIdentityRejectsWhenNoIssuedUtc() public async Task OnValidateIdentityRejectsWhenNoIssuedUtc()
{ {
var user = new TestUser("test"); var user = new TestUser { UserName = "test" };
var httpContext = new Mock<HttpContext>();
var userManager = MockHelpers.MockUserManager<TestUser>(); var manager = SetupUserManager(user);
var identityOptions = new Mock<IOptions<IdentityOptions>>(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
var options = new Mock<IOptions<SecurityStampValidatorOptions>>(); var context = new Mock<HttpContext>();
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object); var roleManager = MockHelpers.MockRoleManager<TestRole>();
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
var services = new ServiceCollection(); var options = new Mock<IOptions<IdentityOptions>>();
services.AddSingleton(options.Object); options.Setup(a => a.Value).Returns(new IdentityOptions());
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock())); var securityStampOptions = new Mock<IOptions<SecurityStampValidatorOptions>>();
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = new MockLoggerFactory();
var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new Mock<SignInManager<TestUser>>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) };
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var principal = new ClaimsPrincipal(id);
//because the request fails the create user principal is never called and therefore not verifiable
//helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
//helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable();
helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(helper.Object);
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory));
context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
// testing the ticket UTC setting, in this case lack of setting
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties(), new AuthenticationProperties(),
IdentityConstants.ApplicationScheme); IdentityConstants.ApplicationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Properties); var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal); Assert.NotNull(cookieContext.Properties);
await SecurityStampValidator.ValidatePrincipalAsync(context); Assert.NotNull(cookieContext.Options);
Assert.Null(context.Principal); Assert.NotNull(cookieContext.Principal);
signInManager.VerifyAll();
await SecurityStampValidator.ValidatePrincipalAsync(cookieContext);
Assert.Null(cookieContext.Principal);
helper.VerifyAll();
} }
[Fact] [Fact]
public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired() public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired()
{ {
var user = new TestUser("test"); var user = new TestUser { UserName = "test" };
var httpContext = new Mock<HttpContext>();
var userManager = MockHelpers.MockUserManager<TestUser>(); var manager = SetupUserManager(user);
var identityOptions = new Mock<IOptions<IdentityOptions>>(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
var claimsManager = new Mock<IUserClaimsPrincipalFactory<TestUser>>();
var options = new Mock<IOptions<SecurityStampValidatorOptions>>(); var context = new Mock<HttpContext>();
options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.FromDays(1) });
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object); var roleManager = MockHelpers.MockRoleManager<TestRole>();
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 options = new Mock<IOptions<IdentityOptions>>();
var services = new ServiceCollection(); options.Setup(a => a.Value).Returns(new IdentityOptions());
services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object); var securityStampOptions = new Mock<IOptions<SecurityStampValidatorOptions>>();
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock())); securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero });
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = new MockLoggerFactory();
var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new Mock<SignInManager<TestUser>>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) };
var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var principal = new ClaimsPrincipal(id);
//because the request fails the create user principal is never called and therefore not verifiable
//helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable();
//helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(user).Verifiable();
//helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(default(TestUser)).Verifiable();
helper.Setup(s => s.SignInAsync(user, false, null)).Throws(new Exception("Shouldn't be called"));
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(helper.Object);
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory));
context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow },
IdentityConstants.ApplicationScheme); IdentityConstants.ApplicationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Properties); var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket);
Assert.NotNull(context.Options); Assert.NotNull(cookieContext.Properties);
Assert.NotNull(context.Principal); Assert.NotNull(cookieContext.Options);
await SecurityStampValidator.ValidatePrincipalAsync(context); Assert.NotNull(cookieContext.Principal);
Assert.NotNull(context.Principal); await SecurityStampValidator.ValidatePrincipalAsync(cookieContext);
Assert.NotNull(cookieContext.Principal);
} }
} }
} }
@@ -7,6 +7,7 @@ using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNetCore.Identity.MongoDbCore.IntegrationTests;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -68,13 +69,13 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact] [Fact]
public void ConstructorNullChecks() public void ConstructorNullChecks()
{ {
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<TestUser>(null, null, null, null, null, null)); Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<TestUser>(null, null, null, null, null, null, null));
var userManager = MockHelpers.MockUserManager<TestUser>().Object; var userManager = MockHelpers.MockUserManager<TestUser>().Object;
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<TestUser>(userManager, null, null, null, null, null)); Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<TestUser>(userManager, null, null, null, null, null, null));
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
var context = new Mock<HttpContext>(); var context = new Mock<HttpContext>();
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<TestUser>(userManager, contextAccessor.Object, null, null, null, null)); Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<TestUser>(userManager, contextAccessor.Object, null, null, null, null, null));
} }
//[Fact] //[Fact]
@@ -122,9 +123,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>(); var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(identityOptions); options.Setup(a => a.Value).Returns(identityOptions);
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object); var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder(); var loggerFactory = new MockLoggerFactory();
var logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore); var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock<IAuthenticationSchemeProvider>().Object); var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
@@ -132,7 +133,7 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert // Assert
Assert.False(result.Succeeded); Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut); Assert.True(result.IsLockedOut);
Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString()); Assert.Contains($"User {user.Id} is currently locked out.", loggerFactory.LogStore.ToString());
manager.Verify(); manager.Verify();
} }
@@ -153,9 +154,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>(); var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(identityOptions); options.Setup(a => a.Value).Returns(identityOptions);
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object); var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder(); var loggerFactory = new MockLoggerFactory();
var logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore); var logger = loggerFactory.CreateLogger<SignInManager<TestUser>>();
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock<IAuthenticationSchemeProvider>().Object); var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
// Act // Act
var result = await helper.CheckPasswordSignInAsync(user, "bogus", false); var result = await helper.CheckPasswordSignInAsync(user, "bogus", false);
@@ -163,7 +164,7 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert // Assert
Assert.False(result.Succeeded); Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut); Assert.True(result.IsLockedOut);
Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString()); Assert.Contains($"User {user.Id} is currently locked out.", loggerFactory.LogStore.ToString());
manager.Verify(); manager.Verify();
} }
@@ -177,7 +178,7 @@ namespace Microsoft.AspNetCore.Identity.Test
return manager; return manager;
} }
private static SignInManager<TestUser> SetupSignInManager(UserManager<TestUser> manager, HttpContext context, StringBuilder logStore = null, IdentityOptions identityOptions = null) private static SignInManager<TestUser> SetupSignInManager(UserManager<TestUser> manager, HttpContext context, MockLoggerFactory factory, IdentityOptions identityOptions = null)
{ {
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(context); contextAccessor.Setup(a => a.HttpContext).Returns(context);
@@ -186,8 +187,20 @@ namespace Microsoft.AspNetCore.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>(); var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(identityOptions); options.Setup(a => a.Value).Returns(identityOptions);
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager, roleManager.Object, options.Object); 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); var sm = new SignInManager<TestUser>(manager, contextAccessor.Object, claimsFactory, options.Object,factory.CreateLogger<SignInManager<TestUser>>(), new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
sm.Logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore ?? new StringBuilder()).Object; return sm;
}
private static Mock<SignInManager<TestUser>> MockSignInManager(UserManager<TestUser> manager, HttpContext context, MockLoggerFactory factory, 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 Mock<SignInManager<TestUser>>(manager, contextAccessor.Object, claimsFactory, options.Object, factory.CreateLogger<SignInManager<TestUser>>(), new Mock<IAuthenticationSchemeProvider>().Object, new Mock<IUserConfirmation<TestUser>>().Object);
return sm; return sm;
} }
@@ -206,7 +219,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
SetupSignIn(context, auth, user.Id, isPersistent); SetupSignIn(context, auth, user.Id, isPersistent);
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
@@ -230,8 +245,8 @@ namespace Microsoft.AspNetCore.Identity.Test
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
SetupSignIn(context, auth, user.Id, false); SetupSignIn(context, auth, user.Id, false);
var helper = SetupSignInManager(manager.Object, context); MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
@@ -256,8 +271,8 @@ namespace Microsoft.AspNetCore.Identity.Test
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
SetupSignIn(context, auth); SetupSignIn(context, auth);
var helper = SetupSignInManager(manager.Object, context); MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
@@ -288,7 +303,10 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).ReturnsAsync(new string[1] { "Fake" }).Verifiable(); manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).ReturnsAsync(new string[1] { "Fake" }).Verifiable();
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
var auth = MockAuth(context); var auth = MockAuth(context);
auth.Setup(a => a.SignInAsync(context, IdentityConstants.TwoFactorUserIdScheme, auth.Setup(a => a.SignInAsync(context, IdentityConstants.TwoFactorUserIdScheme,
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id), It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id),
@@ -326,7 +344,9 @@ namespace Microsoft.AspNetCore.Identity.Test
} }
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
if (bypass) if (bypass)
{ {
@@ -473,7 +493,10 @@ namespace Microsoft.AspNetCore.Identity.Test
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
SetupSignIn(context, auth, user.Id, isPersistent, loginProvider); SetupSignIn(context, auth, user.Id, isPersistent, loginProvider);
// Act // Act
@@ -493,6 +516,7 @@ namespace Microsoft.AspNetCore.Identity.Test
// Setup // Setup
var user = new TestUser { UserName = "Foo" }; var user = new TestUser { UserName = "Foo" };
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var services = new ServiceCollection();
var auth = MockAuth(context); var auth = MockAuth(context);
var loginProvider = "loginprovider"; var loginProvider = "loginprovider";
var id = new ClaimsIdentity(); var id = new ClaimsIdentity();
@@ -503,25 +527,26 @@ namespace Microsoft.AspNetCore.Identity.Test
// REVIEW: auth changes we lost the ability to mock is persistent // REVIEW: auth changes we lost the ability to mock is persistent
//var properties = new AuthenticationProperties { IsPersistent = isPersistent }; //var properties = new AuthenticationProperties { IsPersistent = isPersistent };
var authResult = AuthenticateResult.NoResult(); var authResult = AuthenticateResult.NoResult();
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme))
.Returns(Task.FromResult(authResult)).Verifiable(); auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme)).Returns(Task.FromResult(authResult)).Verifiable();
var manager = SetupUserManager(user); 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 using (MockLoggerFactory loggerFactory = new MockLoggerFactory())
await signInManager.Object.RefreshSignInAsync(user); {
var signInManager = MockSignInManager(manager.Object, context, loggerFactory, null);
// Assert signInManager.CallBase = true; // need this magic!
auth.Verify();
signInManager.Verify(); signInManager.Setup(s => s.SignInWithClaimsAsync(user, It.IsAny<AuthenticationProperties>(), It.IsAny<List<Claim>>())).Returns(Task.FromResult(0)).Verifiable();
// Act
await signInManager.Object.RefreshSignInAsync(user);
// Assert
auth.Verify();
signInManager.Verify();
}
} }
//[Theory] //[Theory]
@@ -606,7 +631,10 @@ namespace Microsoft.AspNetCore.Identity.Test
var manager = SetupUserManager(user); var manager = SetupUserManager(user);
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
auth.Setup(a => a.SignInAsync( auth.Setup(a => a.SignInAsync(
context, context,
IdentityConstants.TwoFactorRememberMeScheme, IdentityConstants.TwoFactorRememberMeScheme,
@@ -644,9 +672,12 @@ namespace Microsoft.AspNetCore.Identity.Test
SetupSignIn(context, auth); SetupSignIn(context, auth);
var id = new ClaimsIdentity(IdentityConstants.TwoFactorRememberMeScheme); var id = new ClaimsIdentity(IdentityConstants.TwoFactorRememberMeScheme);
id.AddClaim(new Claim(ClaimTypes.Name, user.Id)); id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorRememberMeScheme))
auth.Setup(a => a.AuthenticateAsync(It.IsAny<HttpContext>(), IdentityConstants.TwoFactorRememberMeScheme))
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), null, IdentityConstants.TwoFactorRememberMeScheme))).Verifiable(); .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), null, IdentityConstants.TwoFactorRememberMeScheme))).Verifiable();
var helper = SetupSignInManager(manager.Object, context);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
@@ -664,6 +695,15 @@ namespace Microsoft.AspNetCore.Identity.Test
return auth; return auth;
} }
private Mock<IAuthenticationService> MockAuth(ServiceCollection services)
{
var auth = new Mock<IAuthenticationService>();
services.AddSingleton(auth.Object);
return auth;
}
[Fact] [Fact]
public async Task SignOutCallsContextResponseSignOut() public async Task SignOutCallsContextResponseSignOut()
{ {
@@ -671,10 +711,11 @@ namespace Microsoft.AspNetCore.Identity.Test
var manager = MockHelpers.TestUserManager<TestUser>(); var manager = MockHelpers.TestUserManager<TestUser>();
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var auth = MockAuth(context); var auth = MockAuth(context);
var loggerFactory = new MockLoggerFactory();
auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ApplicationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable(); 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.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(); auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ExternalScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
var helper = SetupSignInManager(manager, context, null, manager.Options); var helper = SetupSignInManager(manager, context, loggerFactory, manager.Options);
// Act // Act
await helper.SignOutAsync(); await helper.SignOutAsync();
@@ -693,8 +734,10 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
var context = new Mock<HttpContext>(); var context = new Mock<HttpContext>();
var logStore = new StringBuilder();
var helper = SetupSignInManager(manager.Object, context.Object, logStore); MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
var checkResult = await helper.CheckPasswordSignInAsync(user, "bogus", false); var checkResult = await helper.CheckPasswordSignInAsync(user, "bogus", false);
@@ -702,7 +745,7 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert // Assert
Assert.False(result.Succeeded); Assert.False(result.Succeeded);
Assert.False(checkResult.Succeeded); Assert.False(checkResult.Succeeded);
Assert.Contains($"User {user.Id} failed to provide the correct password.", logStore.ToString()); Assert.Contains($"User {user.Id} failed to provide the correct password.", loggerFactory.LogStore.ToString());
manager.Verify(); manager.Verify();
context.Verify(); context.Verify();
} }
@@ -714,7 +757,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var manager = MockHelpers.MockUserManager<TestUser>(); var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.FindByNameAsync("bogus")).ReturnsAsync(default(TestUser)).Verifiable(); manager.Setup(m => m.FindByNameAsync("bogus")).ReturnsAsync(default(TestUser)).Verifiable();
var context = new Mock<HttpContext>(); var context = new Mock<HttpContext>();
var helper = SetupSignInManager(manager.Object, context.Object);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false); var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
@@ -741,7 +786,9 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout)); manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
var context = new Mock<HttpContext>(); var context = new Mock<HttpContext>();
var helper = SetupSignInManager(manager.Object, context.Object);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory);
// Act // Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true); var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
@@ -768,7 +815,9 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout)); manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
var context = new Mock<HttpContext>(); var context = new Mock<HttpContext>();
var helper = SetupSignInManager(manager.Object, context.Object);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory);
// Act // Act
var result = await helper.CheckPasswordSignInAsync(user, "bogus", true); var result = await helper.CheckPasswordSignInAsync(user, "bogus", true);
@@ -802,8 +851,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var identityOptions = new IdentityOptions(); var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedEmail = true; identityOptions.SignIn.RequireConfirmedEmail = true;
var logStore = new StringBuilder(); var logStore = new StringBuilder();
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions);
MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory, identityOptions);
// Act // Act
var result = await helper.PasswordSignInAsync(user, "password", false, false); var result = await helper.PasswordSignInAsync(user, "password", false, false);
@@ -811,7 +861,7 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(confirmed, result.Succeeded); Assert.Equal(confirmed, result.Succeeded);
Assert.NotEqual(confirmed, result.IsNotAllowed); Assert.NotEqual(confirmed, result.IsNotAllowed);
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email.")); Assert.Equal(confirmed, !loggerFactory.LogStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email."));
manager.Verify(); manager.Verify();
auth.Verify(); auth.Verify();
@@ -846,8 +896,9 @@ namespace Microsoft.AspNetCore.Identity.Test
var identityOptions = new IdentityOptions(); var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedPhoneNumber = true; identityOptions.SignIn.RequireConfirmedPhoneNumber = true;
var logStore = new StringBuilder();
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions); MockLoggerFactory loggerFactory = new MockLoggerFactory();
var helper = SetupSignInManager(manager.Object, context, loggerFactory, identityOptions);
// Act // Act
var result = await helper.PasswordSignInAsync(user, "password", false, false); var result = await helper.PasswordSignInAsync(user, "password", false, false);
@@ -855,7 +906,7 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert // Assert
Assert.Equal(confirmed, result.Succeeded); Assert.Equal(confirmed, result.Succeeded);
Assert.NotEqual(confirmed, result.IsNotAllowed); Assert.NotEqual(confirmed, result.IsNotAllowed);
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number.")); Assert.Equal(confirmed, !loggerFactory.LogStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number."));
manager.Verify(); manager.Verify();
auth.Verify(); auth.Verify();
} }
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.Extensions.Logging;
namespace AspNetCore.Identity.MongoDbCore.IntegrationTests
{
public class MockLoggerFactory : ILoggerFactory
{
public StringBuilder LogStore;
public List<ILogger> Loggers = new List<ILogger>();
public MockLoggerFactory()
{
LogStore = new StringBuilder();
}
public void AddProvider(ILoggerProvider provider)
{
}
public ILogger CreateLogger(string categoryName)
{
var logger = new MockLogger(categoryName, LogStore);
Loggers.Add(logger);
return logger;
}
public ILogger<T> CreateLogger<T>() where T : class
{
//var logger = MockHelpers.MockILogger<T>(LogStore).Object;
var logger = new MockLogger<T>(LogStore);
Loggers.Add(logger);
return logger;
}
public void Dispose()
{
}
}
public class MockLogger : MockLogger<object>
{
public String Name { get; protected set; }
public MockLogger(string name, StringBuilder store) : base(store)
{
Name = name;
}
}
public class MockLogger<T> : ILogger<T>
{
public StringBuilder LogMessages;
public MockLogger(StringBuilder store)
{
LogMessages = store;
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (formatter != null)
{
LogMessages.Append(formatter(state, exception));
}
else
{
LogMessages.Append(state.ToString());
}
}
}
}
@@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var manager = CreateManager(); var manager = CreateManager();
var username = "Create" + Guid.NewGuid().ToString(); var username = "Create" + Guid.NewGuid().ToString();
var user = CreateTestUser(username, useNamePrefixAsUserName: true); var user = CreateTestUser(username, useNamePrefixAsUserName: true);
var stamp = await manager.GetSecurityStampAsync(user); Assert.Null(user.SecurityStamp);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
Assert.NotNull(await manager.GetSecurityStampAsync(user)); Assert.NotNull(await manager.GetSecurityStampAsync(user));
} }
@@ -947,10 +947,12 @@ namespace Microsoft.AspNetCore.Identity.Test
} }
var manager = CreateManager(); var manager = CreateManager();
var user = CreateTestUser(); var user = CreateTestUser();
Assert.Null(await manager.GetSecurityStampAsync(user)); var originalStamp = user.SecurityStamp;
//Update to library, can no longer test for null; throws an exceptoin
Assert.Null(user.SecurityStamp);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
var stamp = await manager.GetSecurityStampAsync(user); var stamp = await manager.GetSecurityStampAsync(user);
Assert.NotNull(stamp); Assert.NotEqual(originalStamp, stamp);
IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
} }