Port ClaimsIdentityFactory and more tests

This commit is contained in:
Hao Kung 2014-03-19 15:08:43 -07:00
parent 7adb28a460
commit 5e36691909
7 changed files with 361 additions and 35 deletions

View File

@ -0,0 +1,107 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Creates a ClaimsIdentity from a User
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TKey"></typeparam>
public class ClaimsIdentityFactory<TUser, TKey> : IClaimsIdentityFactory<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
/// <summary>
/// ClaimType used for the security stamp by default
/// </summary>
public const string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
/// <summary>
/// Constructor
/// </summary>
public ClaimsIdentityFactory()
{
RoleClaimType = ClaimsIdentity.DefaultRoleClaimType;
UserIdClaimType = ClaimTypes.NameIdentifier;
UserNameClaimType = ClaimsIdentity.DefaultNameClaimType;
SecurityStampClaimType = DefaultSecurityStampClaimType;
}
/// <summary>
/// Claim type used for role claims
/// </summary>
public string RoleClaimType { get; set; }
/// <summary>
/// Claim type used for the user name
/// </summary>
public string UserNameClaimType { get; set; }
/// <summary>
/// Claim type used for the user id
/// </summary>
public string UserIdClaimType { get; set; }
/// <summary>
/// Claim type used for the user security stamp
/// </summary>
public string SecurityStampClaimType { get; set; }
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <param name="authenticationType"></param>
/// <returns></returns>
public virtual async Task<ClaimsIdentity> Create(UserManager<TUser, TKey> manager, TUser user,
string authenticationType)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
id.AddClaim(new Claim(UserIdClaimType, ConvertIdToString(user.Id), ClaimValueTypes.String));
id.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
if (manager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(SecurityStampClaimType,
await manager.GetSecurityStamp(user.Id).ConfigureAwait(false)));
}
if (manager.SupportsUserRole)
{
var roles = await manager.GetRoles(user.Id).ConfigureAwait(false);
foreach (var roleName in roles)
{
id.AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String));
}
}
if (manager.SupportsUserClaim)
{
id.AddClaims(await manager.GetClaims(user.Id).ConfigureAwait(false));
}
return id;
}
/// <summary>
/// Convert the key to a string, by default just calls .ToString()
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public virtual string ConvertIdToString(TKey key)
{
if (key == null || key.Equals(default(TKey)))
{
return null;
}
return key.ToString();
}
}
}

View File

@ -39,8 +39,8 @@ namespace Microsoft.AspNet.Identity
PasswordHasher = serviceProvider.GetService<IPasswordHasher>();
UserValidator = serviceProvider.GetService<IUserValidator<TUser, TKey>>();
PasswordValidator = serviceProvider.GetService<IPasswordValidator>();
ClaimsIdentityFactory = serviceProvider.GetService<IClaimsIdentityFactory<TUser, TKey>>();
//TODO: Store = serviceProvider.GetService<IUserStore<TUser, TKey>>();
//TODO: ClaimsIdentityFactory = serviceProvider.GetService<IClaimsIdentityFactory<TUser, TKey>>();
// TODO: maybe each optional store as well? Email and SMS services?
}
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Identity
Store = store;
UserValidator = new UserValidator<TUser, TKey>();
PasswordHasher = new PasswordHasher();
//TODO: ClaimsIdentityFactory = new ClaimsIdentityFactory<TUser, TKey>();
ClaimsIdentityFactory = new ClaimsIdentityFactory<TUser, TKey>();
}
/// <summary>

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
}
[Fact]
public async Task ValidatorCanBlockCreate()
public async Task UserValidatorCanBlockCreate()
{
var manager = CreateManager();
var user = new InMemoryUser("CreateBlocked");
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
}
[Fact]
public async Task ValidatorCanBlockUpdate()
public async Task UserValidatorCanBlockUpdate()
{
var manager = CreateManager();
var user = new InMemoryUser("UpdateBlocked");
@ -51,6 +51,50 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
IdentityResultAssert.IsFailure(await manager.Update(user), AlwaysBadValidator.ErrorMessage);
}
[Theory]
[InlineData("")]
[InlineData(null)]
public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email)
{
var manager = CreateManager();
var user = new InMemoryUser("UpdateBlocked") { Email = email };
manager.UserValidator = new UserValidator<InMemoryUser, string> { RequireUniqueEmail = true };
IdentityResultAssert.IsFailure(await manager.Create(user), "Email cannot be null or empty.");
}
#if NET45
[Theory]
[InlineData("@@afd")]
[InlineData("bogus")]
public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email)
{
var manager = CreateManager();
var user = new InMemoryUser("UpdateBlocked") { Email = email };
manager.UserValidator = new UserValidator<InMemoryUser, string> { RequireUniqueEmail = true };
IdentityResultAssert.IsFailure(await manager.Create(user), "Email '"+email+"' is invalid.");
}
#endif
[Fact]
public async Task PasswordValidatorCanBlockAddPassword()
{
var manager = CreateManager();
var user = new InMemoryUser("AddPasswordBlocked");
IdentityResultAssert.IsSuccess(await manager.Create(user));
manager.PasswordValidator = new AlwaysBadValidator();
IdentityResultAssert.IsFailure(await manager.AddPassword(user.Id, "password"), AlwaysBadValidator.ErrorMessage);
}
[Fact]
public async Task PasswordValidatorCanBlockChangePassword()
{
var manager = CreateManager();
var user = new InMemoryUser("ChangePasswordBlocked");
IdentityResultAssert.IsSuccess(await manager.Create(user, "password"));
manager.PasswordValidator = new AlwaysBadValidator();
IdentityResultAssert.IsFailure(await manager.ChangePassword(user.Id, "password", "new"), AlwaysBadValidator.ErrorMessage);
}
[Fact]
public async Task CanCreateUserNoPassword()
{
@ -291,6 +335,43 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
Assert.Null(usersQ.FirstOrDefault(u => u.UserName == "bogus"));
}
[Fact]
public async Task ClaimsIdentityCreatesExpectedClaims()
{
var manager = CreateManager();
var role = CreateRoleManager();
var user = new InMemoryUser("Hao");
IdentityResultAssert.IsSuccess(await manager.Create(user));
IdentityResultAssert.IsSuccess(await role.Create(new InMemoryRole("Admin")));
IdentityResultAssert.IsSuccess(await role.Create(new InMemoryRole("Local")));
IdentityResultAssert.IsSuccess(await manager.AddToRole(user.Id, "Admin"));
IdentityResultAssert.IsSuccess(await manager.AddToRole(user.Id, "Local"));
Claim[] userClaims =
{
new Claim("Whatever", "Value"),
new Claim("Whatever2", "Value2")
};
foreach (var c in userClaims)
{
IdentityResultAssert.IsSuccess(await manager.AddClaim(user.Id, c));
}
var identity = await manager.CreateIdentity(user, "test");
var claimsFactory = manager.ClaimsIdentityFactory as ClaimsIdentityFactory<InMemoryUser, string>;
Assert.NotNull(claimsFactory);
var claims = identity.Claims;
Assert.NotNull(claims);
Assert.True(
claims.Any(c => c.Type == claimsFactory.UserNameClaimType && c.Value == user.UserName));
Assert.True(claims.Any(c => c.Type == claimsFactory.UserIdClaimType && c.Value == user.Id));
Assert.True(claims.Any(c => c.Type == claimsFactory.RoleClaimType && c.Value == "Admin"));
Assert.True(claims.Any(c => c.Type == claimsFactory.RoleClaimType && c.Value == "Local"));
foreach (var cl in userClaims)
{
Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
}
}
[Fact]
public async Task ConfirmEmailFalseByDefaultTest()
{
@ -331,7 +412,6 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
}
}
[Fact]
public async Task CanResetPasswordWithStaticTokenProvider()
{
@ -351,6 +431,43 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
Assert.NotEqual(stamp, user.SecurityStamp);
}
[Fact]
public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
{
var manager = CreateManager();
manager.UserTokenProvider = new StaticTokenProvider();
var user = new InMemoryUser("ResetPasswordTest");
const string password = "password";
const string newPassword = "newpassword";
IdentityResultAssert.IsSuccess(await manager.Create(user, password));
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
var token = await manager.GeneratePasswordResetToken(user.Id);
Assert.NotNull(token);
manager.PasswordValidator = new AlwaysBadValidator();
IdentityResultAssert.IsFailure(await manager.ResetPassword(user.Id, token, newPassword), AlwaysBadValidator.ErrorMessage);
Assert.NotNull(await manager.Find(user.UserName, password));
Assert.Equal(user, await manager.Find(user.UserName, password));
Assert.Equal(stamp, user.SecurityStamp);
}
[Fact]
public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
{
var manager = CreateManager();
manager.UserTokenProvider = new StaticTokenProvider();
var user = new InMemoryUser("ResetPasswordTest");
const string password = "password";
const string newPassword = "newpassword";
IdentityResultAssert.IsSuccess(await manager.Create(user, password));
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsFailure(await manager.ResetPassword(user.Id, "bogus", newPassword), "Invalid token.");
Assert.NotNull(await manager.Find(user.UserName, password));
Assert.Equal(user, await manager.Find(user.UserName, password));
Assert.Equal(stamp, user.SecurityStamp);
}
[Fact]
public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
{
@ -592,7 +709,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
Assert.True(await manager.RoleExists(role.Name));
}
private class AlwaysBadValidator : IUserValidator<InMemoryUser, string>, IRoleValidator<InMemoryRole, string>
private class AlwaysBadValidator : IUserValidator<InMemoryUser, string>, IRoleValidator<InMemoryRole, string>, IPasswordValidator
{
public const string ErrorMessage = "I'm Bad.";
@ -605,6 +722,11 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
{
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
}
public Task<IdentityResult> Validate(string password)
{
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
}
}
[Fact]
@ -1041,6 +1163,16 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
Assert.True(await manager.VerifyTwoFactorToken(user.Id, factorId, token));
}
[Fact]
public async Task NotifyWithUnknownProviderFails()
{
var manager = CreateManager();
var user = new InMemoryUser("NotifyFail");
IdentityResultAssert.IsSuccess(await manager.Create(user));
await ExceptionAssert.ThrowsAsync<NotSupportedException>(async () => await manager.NotifyTwoFactorToken(user.Id, "Bogus", "token"), "No IUserTwoFactorProvider for 'Bogus' is registered.");
}
//[Fact]
//public async Task EmailTokenFactorWithFormatTest()
//{

View File

@ -0,0 +1,91 @@
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Testing;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
{
public class ClaimsIdentityFactoryTest
{
[Fact]
public async Task CreateIdentityNullChecks()
{
var factory = new ClaimsIdentityFactory<TestUser, string>();
var manager = new UserManager<TestUser, string>(new NoopUserStore());
await Assert.ThrowsAsync<ArgumentNullException>("manager",
async () => await factory.Create(null, null, "whatever"));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await factory.Create(manager, null, "whatever"));
await Assert.ThrowsAsync<ArgumentNullException>("value",
async () => await factory.Create(manager, new TestUser(), null));
}
[Fact]
public void ConvertIdToStringWithDefaultStringReturnsNull()
{
var factory = new ClaimsIdentityFactory<TestUser, string>();
Assert.Null(factory.ConvertIdToString(default(string)));
}
[Fact]
public void ConvertIdToStringWithDefaultIntReturnsNull()
{
var factory = new ClaimsIdentityFactory<TestUser<int>, int>();
Assert.Null(factory.ConvertIdToString(default(int)));
}
[Fact]
public void ConvertIdToStringWithDefaultGuidReturnsNull()
{
var factory = new ClaimsIdentityFactory<TestUser<Guid>, Guid>();
Assert.Null(factory.ConvertIdToString(default(Guid)));
}
// TODO: Need Mock (test in InMemory for now)
//[Fact]
//public async Task ClaimsIdentityTest()
//{
// var db = UnitTestHelper.CreateDefaultDb();
// var manager = new UserManager<TestUser>(new UserStore<TestUser>(db));
// var role = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(db));
// var user = new TestUser("Hao");
// UnitTestHelper.IsSuccess(await manager.CreateAsync(user));
// UnitTestHelper.IsSuccess(await role.CreateAsync(new IdentityRole("Admin")));
// UnitTestHelper.IsSuccess(await role.CreateAsync(new IdentityRole("Local")));
// UnitTestHelper.IsSuccess(await manager.AddToRoleAsync(user.Id, "Admin"));
// UnitTestHelper.IsSuccess(await manager.AddToRoleAsync(user.Id, "Local"));
// Claim[] userClaims =
// {
// new Claim("Whatever", "Value"),
// new Claim("Whatever2", "Value2")
// };
// foreach (var c in userClaims)
// {
// UnitTestHelper.IsSuccess(await manager.AddClaimAsync(user.Id, c));
// }
// var identity = await manager.CreateIdentityAsync(user, "test");
// var claimsFactory = manager.ClaimsIdentityFactory as ClaimsIdentityFactory<TestUser, string>;
// Assert.NotNull(claimsFactory);
// var claims = identity.Claims;
// Assert.NotNull(claims);
// Assert.True(
// claims.Any(c => c.Type == claimsFactory.UserNameClaimType && c.Value == user.UserName));
// Assert.True(claims.Any(c => c.Type == claimsFactory.UserIdClaimType && c.Value == user.Id));
// Assert.True(claims.Any(c => c.Type == claimsFactory.RoleClaimType && c.Value == "Admin"));
// Assert.True(claims.Any(c => c.Type == claimsFactory.RoleClaimType && c.Value == "Local"));
// Assert.True(
// claims.Any(
// c =>
// c.Type == ClaimsIdentityFactory<TestUser>.IdentityProviderClaimType &&
// c.Value == ClaimsIdentityFactory<TestUser>.DefaultIdentityProviderClaimValue));
// foreach (var cl in userClaims)
// {
// Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
// }
//}
}
}

View File

@ -11,8 +11,7 @@ namespace Microsoft.AspNet.Identity.Test
where TUser : class,IUser<TKey>
where TKey : IEquatable<TKey>
{
var serviceCollection = new ServiceCollection();
serviceCollection.Add(TestServices.DefaultServices<TUser, TKey>());
var serviceCollection = new ServiceCollection { DefaultServices<TUser, TKey>() };
return serviceCollection.BuildServiceProvider();
}
@ -25,6 +24,7 @@ namespace Microsoft.AspNet.Identity.Test
new ServiceDescriptor<IPasswordValidator, PasswordValidator>(),
new ServiceDescriptor<IUserValidator<TUser, TKey>, UserValidator<TUser, TKey>>(),
new ServiceDescriptor<IPasswordHasher, PasswordHasher>(),
new ServiceDescriptor<IClaimsIdentityFactory<TUser, TKey>, ClaimsIdentityFactory<TUser, TKey>>(),
};
}
@ -52,30 +52,5 @@ namespace Microsoft.AspNet.Identity.Test
get { return null; }
}
}
public class ServiceInstanceDescriptor<TService> : IServiceDescriptor
{
public ServiceInstanceDescriptor(object instance)
{
ImplementationInstance = instance;
}
public LifecycleKind Lifecycle
{
get { return LifecycleKind.Singleton; }
}
public Type ServiceType
{
get { return typeof(TService); }
}
public Type ImplementationType
{
get { return null; }
}
public object ImplementationInstance { get; private set; }
}
}
}

View File

@ -1,9 +1,14 @@
namespace Microsoft.AspNet.Identity.Test
{
public class TestUser : IUser<string>
public class TestUser : TestUser<string>
{
public string Id { get; private set; }
}
public class TestUser<TKey> : IUser<TKey>
{
public TKey Id { get; private set; }
public string UserName { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Runtime;
using System.Security.Claims;
using Microsoft.AspNet.Testing;
using Moq;
@ -38,6 +39,20 @@ namespace Microsoft.AspNet.Identity.Test
// store.VerifyAll();
//}
[Fact]
public async Task CheckPasswordWithNullUserReturnsFalse()
{
var manager = new UserManager<TestUser, string>(new EmptyStore());
Assert.False(await manager.CheckPassword(null, "whatevs"));
}
[Fact]
public async Task FindWithUnknownUserAndPasswordReturnsNull()
{
var manager = new UserManager<TestUser, string>(new EmptyStore());
Assert.Null(await manager.Find("bogus", "whatevs"));
}
[Fact]
public void UsersQueryableFailWhenStoreNotImplemented()
{
@ -310,6 +325,7 @@ namespace Microsoft.AspNet.Identity.Test
{
var manager = new UserManager<TestUser, string>(new NoopUserStore());
manager.Dispose();
Assert.Throws<ObjectDisposedException>(() => manager.ClaimsIdentityFactory);
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.AddClaim("bogus", null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.AddLogin("bogus", null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.AddPassword("bogus", null));