first commit

This commit is contained in:
alexandre-spieser
2017-10-22 00:24:46 +00:00
commit 0dc4240d2c
69 changed files with 17455 additions and 0 deletions
@@ -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>
@@ -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;
}
}
@@ -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));
// }
// }
//}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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));
}
}
}
@@ -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)
{
}
}
}
@@ -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)
{ }
}
}
@@ -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 { }
}
@@ -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
}
@@ -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));
}
}
}
}
@@ -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);
// }
// }
//}
}
@@ -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;
}
}
@@ -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)
{
}
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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);
}
}
}
}
}
@@ -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));
}
}
}
@@ -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();
}
}
}
}
@@ -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));
}
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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();
}
}
}
@@ -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;
}
}
}
}
@@ -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();
}
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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");
}
}
}
}
@@ -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));
}
}
}
}