Initial DI changes from app building

Incorporate feedback and make identity DI friendly everywhere
Also start using the new OptionsSetup
This commit is contained in:
Hao Kung 2014-05-02 17:09:24 -07:00
parent 8e5ac31889
commit 31f76b8d58
57 changed files with 2907 additions and 2105 deletions

View File

@ -0,0 +1,19 @@
using Microsoft.AspNet.Identity.Entity;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
public static class IdentityBuilderExtensions
{
public static IdentityBuilder<TUser, TRole> AddEntity<TUser, TRole>(this IdentityBuilder<TUser, TRole> builder)
where TUser : EntityUser
where TRole : EntityRole
{
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser>>();
builder.Services.AddScoped<UserManager<TUser>, UserManager<TUser>>();
builder.Services.AddScoped<IRoleStore<TRole>, RoleStore<TRole>>();
builder.Services.AddScoped<RoleManager<TRole>, RoleManager<TRole>>();
return builder;
}
}
}

View File

@ -19,6 +19,7 @@ using System;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.Data.Entity;
using Microsoft.Data.SqlServer;
using Microsoft.Data.InMemory;
using Microsoft.Data.Entity.Metadata;
@ -33,7 +34,7 @@ namespace Microsoft.AspNet.Identity.Entity
public class IdentityContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : DbContext
where TUser : EntityUser<TKey, TUserLogin, TUserRole, TUserClaim>
where TRole : EntityRole<TKey, TUserRole> /*, TUserRole*/
where TRole : EntityRole<TKey, TUserRole>
where TUserLogin : IdentityUserLogin<TKey>
where TUserRole : IdentityUserRole<TKey>
where TUserClaim : IdentityUserClaim<TKey>
@ -51,7 +52,7 @@ namespace Microsoft.AspNet.Identity.Entity
protected override void OnConfiguring(EntityConfigurationBuilder builder)
{
//#if NET45
// builder.SqlServerConnectionString(@"Server=(localdb)\v11.0;Database=IdentityDb;Trusted_Connection=True;");
// builder.SqlServerConnectionString(@"Server=(localdb)\v11.0;Database=IdentityDb3;Trusted_Connection=True;");
//#else
builder.UseInMemoryStore();
//#endif

View File

@ -20,20 +20,14 @@
<Content Include="Project.json" />
</ItemGroup>
<ItemGroup>
<<<<<<< HEAD
<Compile Include="IdentityContext.cs" />
<Compile Include="IdentityRole.cs" />
<Compile Include="IdentityUser.cs" />
<Compile Include="IdentityUserClaim.cs" />
<Compile Include="IdentityUserLogin.cs" />
<Compile Include="IdentityUserRole.cs" />
=======
<Compile Include="EntityRole.cs" />
<Compile Include="EntityUser.cs" />
<Compile Include="IdentityContext.cs" />
>>>>>>> POCO
<Compile Include="RoleStore.cs" />
<Compile Include="UserStore.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -24,6 +24,11 @@ using Microsoft.Data.Entity;
namespace Microsoft.AspNet.Identity.Entity
{
public class RoleStore<TRole> : RoleStore<TRole, string> where TRole : EntityRole
{
public RoleStore(DbContext context) : base(context) { }
}
public class RoleStore<TRole, TKey> :
IQueryableRoleStore<TRole>
where TRole : EntityRole

View File

@ -4,9 +4,9 @@
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
"Microsoft.AspNet.Identity": "0.1-alpha-*",
"Microsoft.AspNet.Logging": "0.1-alpha-*",
"Microsoft.Data.Entity": "0.1-alpha-*",
"Microsoft.Data.Relational": "0.1-alpha-*",
"Microsoft.Data.SqlServer": "0.1-alpha-*",
"Microsoft.Data.InMemory": "0.1-alpha-*",
"System.Security.Claims" : "0.1-alpha-*"
},

View File

@ -0,0 +1,19 @@
using Microsoft.AspNet.Identity.InMemory;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
public static class IdentityBuilderExtensions
{
public static IdentityBuilder<TUser, TRole> AddInMemory<TUser, TRole>(this IdentityBuilder<TUser, TRole> builder)
where TUser : IdentityUser
where TRole : IdentityRole
{
builder.Services.AddScoped<IUserStore<TUser>, InMemoryUserStore<TUser>>();
builder.Services.AddScoped<UserManager<TUser>, UserManager<TUser>>();
builder.Services.AddScoped<IRoleStore<TRole>, InMemoryRoleStore<TRole>>();
builder.Services.AddScoped<RoleManager<TRole>, RoleManager<TRole>>();
return builder;
}
}
}

View File

@ -23,5 +23,8 @@
<Compile Include="InMemoryRoleStore.cs" />
<Compile Include="InMemoryUserStore.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -2,10 +2,7 @@
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Identity": "0.1-alpha-*",
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel" : "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
"Microsoft.AspNet.RequestContainer" : "0.1-alpha-*",
"System.Security.Claims": "0.1-alpha-*"
},
"configurations": {

View File

@ -19,6 +19,6 @@ namespace Microsoft.AspNet.Identity.Security
{
public static class DefaultAuthenticationTypes
{
public const string ApplicationCookie = "Microsoft.Aspnet.Identity.Security.Application";
public static readonly string ApplicationCookie = typeof(DefaultAuthenticationTypes).Namespace+".Application";
}
}

View File

@ -20,8 +20,13 @@
<Content Include="Project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="DefaultAuthenticationTypes.cs" />
<Compile Include="IdentityExtensions.cs" />
<Compile Include="SignInManager.cs" />
<Compile Include="SignInStatus.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Open Technologies, Inc.
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -17,13 +17,37 @@
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.DependencyInjection;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.Security
{
public class SignInManager<TUser> where TUser : class
public class SignInManager<TUser> : SignInManager<UserManager<TUser>, TUser> where TUser : class
{
public SignInManager(UserManager<TUser> userManager, IContextAccessor<HttpContext> contextAccessor)
: base(userManager, contextAccessor) { }
}
public class SignInManager<TManager, TUser> where TManager : UserManager<TUser> where TUser : class
{
public SignInManager(TManager userManager, IContextAccessor<HttpContext> contextAccessor)
{
if (userManager == null)
{
throw new ArgumentNullException("userManager");
}
if (contextAccessor == null || contextAccessor.Value == null)
{
throw new ArgumentNullException("contextAccessor");
}
UserManager = userManager;
Context = contextAccessor.Value;
}
// TODO: this should go into some kind of Options/setup
private string _authType;
public string AuthenticationType
{
@ -31,25 +55,16 @@ namespace Microsoft.AspNet.Identity.Security
set { _authType = value; }
}
public UserManager<TUser> UserManager { get; set; }
public HttpContext Context { get; set; }
public TManager UserManager { get; private set; }
public HttpContext Context { get; private set; }
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user)
{
if (UserManager == null)
{
return null;
}
return await UserManager.CreateIdentityAsync(user, AuthenticationType);
}
public virtual async Task SignInAsync(TUser user, bool isPersistent, bool rememberBrowser)
{
if (Context == null)
{
return;
}
// TODO: all the two factor logic/external/rememberBrowser
var userIdentity = await CreateUserIdentityAsync(user);
Context.Response.SignIn(userIdentity, new AuthenticationProperties { IsPersistent = isPersistent });
@ -57,10 +72,6 @@ namespace Microsoft.AspNet.Identity.Security
public virtual void SignOut()
{
if (Context == null)
{
return;
}
Context.Response.SignOut(AuthenticationType);
}
@ -151,10 +162,6 @@ namespace Microsoft.AspNet.Identity.Security
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
if (UserManager == null)
{
return SignInStatus.Failure;
}
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{

View File

@ -0,0 +1,60 @@
using System;
using System.ComponentModel;
using System.Security.Claims;
using Microsoft.AspNet.ConfigurationModel;
namespace Microsoft.AspNet.Identity
{
public class ClaimTypeOptions
{
/// <summary>
/// ClaimType used for the security stamp by default
/// </summary>
public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
public ClaimTypeOptions()
{
Role = ClaimTypes.Role;
SecurityStamp = DefaultSecurityStampClaimType;
UserId = ClaimTypes.NameIdentifier;
UserName = ClaimTypes.Name;
}
public ClaimTypeOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Claim type used for role claims
/// </summary>
public string Role { get; set; }
/// <summary>
/// Claim type used for the user name
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Claim type used for the user id
/// </summary>
public string UserId { get; set; }
/// <summary>
/// Claim type used for the user security stamp
/// </summary>
public string SecurityStamp { get; set; }
public virtual void Copy(ClaimTypeOptions options)
{
if (options == null)
{
return;
}
Role = options.Role;
SecurityStamp = options.SecurityStamp;
UserId = options.UserId;
UserName = options.UserName;
}
}
}

View File

@ -19,6 +19,7 @@ using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
@ -29,42 +30,6 @@ namespace Microsoft.AspNet.Identity
public class ClaimsIdentityFactory<TUser> : IClaimsIdentityFactory<TUser>
where TUser : class
{
/// <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>
/// CreateAsync a ClaimsIdentity from a user
/// </summary>
@ -86,19 +51,19 @@ namespace Microsoft.AspNet.Identity
}
var userId = await manager.GetUserIdAsync(user, cancellationToken);
var userName = await manager.GetUserNameAsync(user, cancellationToken);
var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
id.AddClaim(new Claim(UserIdClaimType, userId));
id.AddClaim(new Claim(UserNameClaimType, userName, ClaimValueTypes.String));
var id = new ClaimsIdentity(authenticationType, manager.Options.ClaimType.UserName, manager.Options.ClaimType.Role);
id.AddClaim(new Claim(manager.Options.ClaimType.UserId, userId));
id.AddClaim(new Claim(manager.Options.ClaimType.UserName, userName, ClaimValueTypes.String));
if (manager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(SecurityStampClaimType, await manager.GetSecurityStampAsync(user, cancellationToken)));
id.AddClaim(new Claim(manager.Options.ClaimType.SecurityStamp, await manager.GetSecurityStampAsync(user, cancellationToken)));
}
if (manager.SupportsUserRole)
{
var roles = await manager.GetRolesAsync(user, cancellationToken);
foreach (var roleName in roles)
{
id.AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String));
id.AddClaim(new Claim(manager.Options.ClaimType.Role, roleName, ClaimValueTypes.String));
}
}
if (manager.SupportsUserClaim)

View File

@ -23,12 +23,12 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Used to validate passwords
/// </summary>
public interface IPasswordValidator
public interface IPasswordValidator<TUser> where TUser : class
{
/// <summary>
/// Validate the item
/// </summary>
/// <returns></returns>
Task<IdentityResult> ValidateAsync(string password, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -22,55 +22,82 @@ namespace Microsoft.AspNet.Identity
{
public class IdentityBuilder<TUser, TRole> where TUser : class where TRole : class
{
private ServiceCollection Services { get; set; }
public ServiceCollection Services { get; private set; }
public IdentityBuilder(ServiceCollection services)
{
Services = services;
}
public IdentityBuilder<TUser, TRole> Use<T>(Func<T> func)
// Rename to Add
public IdentityBuilder<TUser, TRole> AddInstance<T>(Func<T> func)
{
Services.AddInstance<T>(func());
Services.AddInstance(func());
return this;
}
public IdentityBuilder<TUser, TRole> UseIdentity()
public IdentityBuilder<TUser, TRole> AddUserStore(Func<IUserStore<TUser>> func)
{
Services.Add(IdentityServices.GetDefaultUserServices<TUser>());
Services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
return AddInstance(func);
}
public IdentityBuilder<TUser, TRole> AddRoleStore(Func<IRoleStore<TRole>> func)
{
return AddInstance(func);
}
public IdentityBuilder<TUser, TRole> AddPasswordValidator(Func<IPasswordValidator<TUser>> func)
{
return AddInstance(func);
}
public IdentityBuilder<TUser, TRole> AddUserValidator(Func<IUserValidator<TUser>> func)
{
return AddInstance(func);
}
public class OptionsSetup<TOptions> : IOptionsSetup<TOptions>
{
public Action<TOptions> SetupAction { get; private set; }
public OptionsSetup(Action<TOptions> setupAction)
{
if (setupAction == null)
{
throw new ArgumentNullException("setupAction");
}
SetupAction = setupAction;
}
public void Setup(TOptions options)
{
SetupAction(options);
}
public int Order { get; set; }
}
public IdentityBuilder<TUser, TRole> SetupOptions(Action<IdentityOptions> action, int order)
{
Services.AddSetup(new OptionsSetup<IdentityOptions>(action) {Order = order});
return this;
}
public IdentityBuilder<TUser, TRole> UseUserStore(Func<IUserStore<TUser>> func)
public IdentityBuilder<TUser, TRole> SetupOptions(Action<IdentityOptions> action)
{
return Use(func);
return SetupOptions(action, 0);
}
public IdentityBuilder<TUser, TRole> UseRoleStore(Func<IRoleStore<TRole>> func)
public IdentityBuilder<TUser, TRole> AddUserManager<TManager>() where TManager : UserManager<TUser>
{
return Use(func);
}
public IdentityBuilder<TUser, TRole> UsePasswordValidator(Func<IPasswordValidator> func)
{
return Use(func);
}
public IdentityBuilder<TUser, TRole> UseUserValidator(Func<IUserValidator<TUser>> func)
{
return Use(func);
}
public IdentityBuilder<TUser, TRole> UseUserManager<TManager>() where TManager : UserManager<TUser>
{
Services.AddScoped<TManager, TManager>();
Services.AddScoped<TManager>();
return this;
}
public IdentityBuilder<TUser, TRole> UseRoleManager<TManager>() where TManager : RoleManager<TRole>
public IdentityBuilder<TUser, TRole> AddRoleManager<TManager>() where TManager : RoleManager<TRole>
{
Services.AddScoped<TManager, TManager>();
Services.AddScoped<TManager>();
return this;
}
@ -79,10 +106,5 @@ namespace Microsoft.AspNet.Identity
// return Use(func);
//}
public IdentityBuilder<TUser, TRole> UseLockoutPolicy(Func<LockoutPolicy> func)
{
return Use(func);
}
}
}

View File

@ -0,0 +1,89 @@
using System.Reflection;
using Microsoft.AspNet.ConfigurationModel;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Configuration for lockout
/// </summary>
public class IdentityOptions
{
public IdentityOptions()
{
// TODO: Split into sub options
ClaimType = new ClaimTypeOptions();
User = new UserOptions();
Password = new PasswordOptions();
Lockout = new LockoutOptions();
}
public IdentityOptions(IConfiguration config)
{
ClaimType = new ClaimTypeOptions(config.GetSubKey("identity:claimtype"));
User = new UserOptions(config.GetSubKey("identity:user"));
Password = new PasswordOptions(config.GetSubKey("identity:password"));
//Lockout = new LockoutOptions(config.GetSubKey("identity:lockout"));
}
public static void Read(object obj, IConfiguration config)
{
var type = obj.GetType();
var props = type.GetTypeInfo().DeclaredProperties;
foreach (var prop in props)
{
// TODO: handle non string types?
if (!prop.CanWrite)
{
continue;
}
var configValue = config.Get(prop.Name);
if (configValue == null)
{
continue;
}
if (prop.PropertyType == typeof(string))
{
prop.SetValue(obj, configValue);
}
else if (prop.PropertyType == typeof(int))
{
// todo: TryParse/ errors?
prop.SetValue(obj, int.Parse(configValue));
}
else if (prop.PropertyType == typeof(bool))
{
// todo: TryParse/ errors?
prop.SetValue(obj, bool.Parse(configValue));
}
//else if (prop.PropertyType == typeof(TimeSpan))
//{
// // todo: TryParse/ errors?
// prop.SetValue(obj, TimeSpan.Parse(configValue));
//}
}
}
public ClaimTypeOptions ClaimType { get; set; }
public UserOptions User { get; set; }
public PasswordOptions Password { get; set; }
public LockoutOptions Lockout { get; set; }
// TODO: maybe make this internal as its only for tests
public void Copy(IdentityOptions options)
{
if (options == null)
{
return;
}
User.Copy(options.User);
Password.Copy(options.Password);
Lockout.Copy(options.Lockout);
ClaimType.Copy(options.ClaimType);
}
}
}

View File

@ -28,6 +28,7 @@ namespace Microsoft.AspNet.DependencyInjection
{
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
services.AddSingleton<IOptionsAccessor<IdentityOptions>, OptionsAccessor<IdentityOptions>>();
actionBuilder(new IdentityBuilder<TUser, TRole>(services));
return services;
}
@ -35,10 +36,7 @@ namespace Microsoft.AspNet.DependencyInjection
public static ServiceCollection AddIdentity<TUser>(this ServiceCollection services, Action<IdentityBuilder<TUser, IdentityRole>> actionBuilder)
where TUser : class
{
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
services.Add(IdentityServices.GetDefaultRoleServices<IdentityRole>());
actionBuilder(new IdentityBuilder<TUser, IdentityRole>(services));
return services;
return services.AddIdentity<TUser, IdentityRole>(actionBuilder);
}
}
}

View File

@ -38,26 +38,12 @@ namespace Microsoft.AspNet.Identity
var describe = new ServiceDescriber(configuration);
// TODO: review defaults for validators should get picked up from config?
yield return describe.Instance<IUserValidator<TUser>>(new UserValidator<TUser>());
yield return describe.Instance<IPasswordValidator>(new PasswordValidator()
{
RequiredLength = 6,
RequireDigit = true,
RequireLowercase = true,
RequireNonLetterOrDigit = true,
RequireUppercase = true
});
yield return describe.Instance<IPasswordHasher>(new PasswordHasher());
yield return describe.Instance<IClaimsIdentityFactory<TUser>>(new ClaimsIdentityFactory<TUser>());
yield return describe.Instance<LockoutPolicy>(new LockoutPolicy
{
UserLockoutEnabledByDefault = false,
DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5),
MaxFailedAccessAttemptsBeforeLockout = 5
});
yield return describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>();
yield return describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
yield return describe.Transient<IPasswordHasher, PasswordHasher>();
yield return describe.Transient<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser>>();
// TODO: rationalize email/sms/usertoken services
// TODO: configure lockout from config?
}
public static IEnumerable<IServiceDescriptor> GetDefaultRoleServices<TRole>() where TRole : class

View File

@ -0,0 +1,42 @@
using System;
namespace Microsoft.AspNet.Identity
{
public class LockoutOptions
{
public LockoutOptions()
{
DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
EnabledByDefault = false;
MaxFailedAccessAttempts = 5;
}
public void Copy(LockoutOptions options)
{
if (options == null)
{
return;
}
DefaultLockoutTimeSpan = options.DefaultLockoutTimeSpan;
EnabledByDefault = options.EnabledByDefault;
MaxFailedAccessAttempts = options.MaxFailedAccessAttempts;
}
/// <summary>
/// If true, will enable user lockout when users are created
/// </summary>
public bool EnabledByDefault { get; set; }
/// <summary>
/// Number of access attempts allowed for a user before lockout (if enabled)
/// </summary>
public int MaxFailedAccessAttempts { get; set; }
/// <summary>
/// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached
/// </summary>
public TimeSpan DefaultLockoutTimeSpan { get; set; }
}
}

View File

@ -24,8 +24,18 @@
<Compile Include="ClaimsIdentityFactory.cs" />
<Compile Include="Crypto.cs" />
<Compile Include="IClaimsIdentityFactory.cs" />
<Compile Include="IdentityBuilder.cs" />
<Compile Include="IdentityMessage.cs" />
<Compile Include="IdentityOptions.cs" />
<Compile Include="IdentityResult.cs" />
<Compile Include="IdentityRole.cs" />
<Compile Include="IdentityServiceCollectionExtensions.cs" />
<Compile Include="IdentityServices.cs" />
<Compile Include="IdentitySetup.cs" />
<Compile Include="IdentityUser.cs" />
<Compile Include="IdentityUserClaim.cs" />
<Compile Include="IdentityUserLogin.cs" />
<Compile Include="IdentityUserRole.cs" />
<Compile Include="IIdentityMessageService.cs" />
<Compile Include="IPasswordHasher.cs" />
<Compile Include="IPasswordValidator.cs" />
@ -56,5 +66,8 @@
<Compile Include="UserManager.cs" />
<Compile Include="UserValidator.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,59 @@
using Microsoft.AspNet.ConfigurationModel;
namespace Microsoft.AspNet.Identity
{
public class PasswordOptions
{
public PasswordOptions()
{
RequireDigit = true;
RequireLowercase = true;
RequireNonLetterOrDigit = true;
RequireUppercase = true;
RequiredLength = 6;
}
public PasswordOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Minimum required length
/// </summary>
public int RequiredLength { get; set; }
/// <summary>
/// Require a non letter or digit character
/// </summary>
public bool RequireNonLetterOrDigit { get; set; }
/// <summary>
/// Require a lower case letter ('a' - 'z')
/// </summary>
public bool RequireLowercase { get; set; }
/// <summary>
/// Require an upper case letter ('A' - 'Z')
/// </summary>
public bool RequireUppercase { get; set; }
/// <summary>
/// Require a digit ('0' - '9')
/// </summary>
public bool RequireDigit { get; set; }
public virtual void Copy(PasswordOptions options)
{
if (options == null)
{
return;
}
RequireDigit = options.RequireDigit;
RequireLowercase = options.RequireLowercase;
RequireNonLetterOrDigit = options.RequireNonLetterOrDigit;
RequireUppercase = options.RequireUppercase;
RequiredLength = options.RequiredLength;
}
}
}

View File

@ -27,63 +27,44 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Used to validate some basic password policy like length and number of non alphanumerics
/// </summary>
public class PasswordValidator : IPasswordValidator
public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
{
/// <summary>
/// Minimum required length
/// Ensures that the password is of the required length and meets the configured requirements
/// </summary>
public int RequiredLength { get; set; }
/// <summary>
/// Require a non letter or digit character
/// </summary>
public bool RequireNonLetterOrDigit { get; set; }
/// <summary>
/// Require a lower case letter ('a' - 'z')
/// </summary>
public bool RequireLowercase { get; set; }
/// <summary>
/// Require an upper case letter ('A' - 'Z')
/// </summary>
public bool RequireUppercase { get; set; }
/// <summary>
/// Require a digit ('0' - '9')
/// </summary>
public bool RequireDigit { get; set; }
/// <summary>
/// Ensures that the string is of the required length and meets the configured requirements
/// </summary>
/// <param name="item"></param>
/// <param name="password"></param>
/// <param name="manager"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<IdentityResult> ValidateAsync(string item, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
{
if (item == null)
if (password == null)
{
throw new ArgumentNullException("item");
throw new ArgumentNullException("password");
}
if (manager == null)
{
throw new ArgumentNullException("manager");
}
var errors = new List<string>();
if (string.IsNullOrWhiteSpace(item) || item.Length < RequiredLength)
var options = manager.Options.Password;
if (string.IsNullOrWhiteSpace(password) || password.Length < options.RequiredLength)
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PasswordTooShort, RequiredLength));
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PasswordTooShort, options.RequiredLength));
}
if (RequireNonLetterOrDigit && item.All(IsLetterOrDigit))
if (options.RequireNonLetterOrDigit && password.All(IsLetterOrDigit))
{
errors.Add(Resources.PasswordRequireNonLetterOrDigit);
}
if (RequireDigit && !item.Any(IsDigit))
if (options.RequireDigit && !password.Any(IsDigit))
{
errors.Add(Resources.PasswordRequireDigit);
}
if (RequireLowercase && !item.Any(IsLower))
if (options.RequireLowercase && !password.Any(IsLower))
{
errors.Add(Resources.PasswordRequireLower);
}
if (RequireUppercase && !item.Any(IsUpper))
if (options.RequireUppercase && !password.Any(IsUpper))
{
errors.Add(Resources.PasswordRequireUpper);
}

View File

@ -32,24 +32,23 @@ namespace Microsoft.AspNet.Identity
{
private bool _disposed;
public RoleManager(IServiceProvider services)
{
Initialize(services);
}
/// <summary>
/// Constructor
/// Constructor
/// </summary>
/// <param name="services"></param>
/// <param name="store">The IRoleStore is responsible for commiting changes via the UpdateAsync/CreateAsync methods</param>
public RoleManager(IRoleStore<TRole> store)
public RoleManager(IServiceProvider services, IRoleStore<TRole> store)
{
if (store == null)
{
throw new ArgumentNullException("store");
}
var services = new ServiceCollection { IdentityServices.GetDefaultRoleServices<TRole>() };
services.AddInstance<IRoleStore<TRole>>(store);
Initialize(services.BuildServiceProvider());
if (services == null)
{
throw new ArgumentNullException("services");
}
RoleValidator = services.GetService<IRoleValidator<TRole>>() ?? new RoleValidator<TRole>();
Store = store;
}
/// <summary>
@ -90,21 +89,6 @@ namespace Microsoft.AspNet.Identity
}
}
public void Initialize(IServiceProvider services)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
Store = services.GetService<IRoleStore<TRole>>();
if (Store == null)
{
// TODO: what is the right way to enforce required services
throw new InvalidOperationException();
}
RoleValidator = services.GetService<IRoleValidator<TRole>>();
}
/// <summary>
/// Dispose this object
/// </summary>

View File

@ -41,52 +41,34 @@ namespace Microsoft.AspNet.Identity
private TimeSpan _defaultLockout = TimeSpan.Zero;
private bool _disposed;
private IPasswordHasher _passwordHasher;
private LockoutPolicy _lockoutPolicy;
// Needed for mock unit tests
public UserManager() { }
private IdentityOptions _options;
/// <summary>
/// Constructor which takes a service provider
/// Constructor which takes a service provider and user store
/// </summary>
/// <param name="serviceProvider"></param>
public UserManager(IServiceProvider serviceProvider)
{
Initialize(serviceProvider);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="store">The IUserStore is responsible for commiting changes via the UpdateAsync/CreateAsync methods</param>
public UserManager(IUserStore<TUser> store)
{
if (store == null)
{
throw new ArgumentNullException("store");
}
var services = new ServiceCollection { IdentityServices.GetDefaultUserServices<TUser>() };
services.AddInstance<IUserStore<TUser>>(store);
Initialize(services.BuildServiceProvider());
}
public void Initialize(IServiceProvider serviceProvider)
/// <param name="store"></param>
/// <param name="optionsAccessor"></param>
public UserManager(IServiceProvider serviceProvider, IUserStore<TUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor)
{
if (serviceProvider == null)
{
throw new ArgumentNullException("serviceProvider");
}
PasswordHasher = serviceProvider.GetService<IPasswordHasher>();
UserValidator = serviceProvider.GetService<IUserValidator<TUser>>();
PasswordValidator = serviceProvider.GetService<IPasswordValidator>();
ClaimsIdentityFactory = serviceProvider.GetService<IClaimsIdentityFactory<TUser>>();
LockoutPolicy = serviceProvider.GetService<LockoutPolicy>();
Store = serviceProvider.GetService<IUserStore<TUser>>();
if (Store == null)
if (store == null)
{
// TODO: what is the right way to enforce required services
throw new InvalidOperationException();
throw new ArgumentNullException("store");
}
if (optionsAccessor == null || optionsAccessor.Options == null)
{
throw new ArgumentNullException("optionsAccessor");
}
Store = store;
Options = optionsAccessor.Options;
PasswordHasher = serviceProvider.GetService<IPasswordHasher>() ?? new PasswordHasher();
UserValidator = serviceProvider.GetService<IUserValidator<TUser>>() ?? new UserValidator<TUser>();
PasswordValidator = serviceProvider.GetService<IPasswordValidator<TUser>>() ?? new PasswordValidator<TUser>();
ClaimsIdentityFactory = serviceProvider.GetService<IClaimsIdentityFactory<TUser>>() ?? new ClaimsIdentityFactory<TUser>();
// TODO: Email/Sms/Token services
}
@ -124,7 +106,7 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Used to validate passwords before persisting changes
/// </summary>
public IPasswordValidator PasswordValidator { get; set; }
public IPasswordValidator<TUser> PasswordValidator { get; set; }
/// <summary>
/// Used to create claims identities from users
@ -162,30 +144,23 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public IUserTokenProvider<TUser> UserTokenProvider { get; set; }
public LockoutPolicy LockoutPolicy { get; set; }
private bool UserLockoutEnabledByDefault
public IdentityOptions Options
{
get
{
return LockoutPolicy != null && LockoutPolicy.UserLockoutEnabledByDefault;
ThrowIfDisposed();
return _options;
}
}
private int MaxFailedAccessAttemptsBeforeLockout
{
get
set
{
return LockoutPolicy != null ? LockoutPolicy.MaxFailedAccessAttemptsBeforeLockout : 0;
}
}
private TimeSpan DefaultAccountLockoutTimeSpan
{
get
{
return LockoutPolicy != null ? LockoutPolicy.DefaultAccountLockoutTimeSpan : TimeSpan.FromMinutes(5);
ThrowIfDisposed();
if (value == null)
{
throw new ArgumentNullException("value");
}
_options = value;
}
}
/// <summary>
@ -380,7 +355,7 @@ namespace Microsoft.AspNet.Identity
{
return result;
}
if (UserLockoutEnabledByDefault && SupportsUserLockout)
if (Options.Lockout.EnabledByDefault && SupportsUserLockout)
{
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, cancellationToken);
}
@ -673,7 +648,7 @@ namespace Microsoft.AspNet.Identity
{
if (PasswordValidator != null)
{
var result = await PasswordValidator.ValidateAsync(newPassword, cancellationToken);
var result = await PasswordValidator.ValidateAsync(newPassword, this, cancellationToken);
if (!result.Succeeded)
{
return result;
@ -1757,12 +1732,12 @@ namespace Microsoft.AspNet.Identity
}
// If this puts the user over the threshold for lockout, lock them out and reset the access failed count
var count = await store.IncrementAccessFailedCountAsync(user, cancellationToken);
if (count < MaxFailedAccessAttemptsBeforeLockout)
if (count < Options.Lockout.MaxFailedAccessAttempts)
{
return await UpdateAsync(user, cancellationToken);
}
await
store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan), cancellationToken);
store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), cancellationToken);
await store.ResetAccessFailedCountAsync(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}

View File

@ -0,0 +1,38 @@
using Microsoft.AspNet.ConfigurationModel;
namespace Microsoft.AspNet.Identity
{
public class UserOptions
{
public UserOptions()
{
AllowOnlyAlphanumericNames = true;
//User.RequireUniqueEmail = true; // TODO: app decision?
}
public UserOptions(IConfiguration config) : this()
{
IdentityOptions.Read(this, config);
}
/// <summary>
/// Only allow [A-Za-z0-9@_] in UserNames
/// </summary>
public bool AllowOnlyAlphanumericNames { get; set; }
/// <summary>
/// If set, enforces that emails are non empty, valid, and unique
/// </summary>
public bool RequireUniqueEmail { get; set; }
public virtual void Copy(UserOptions options)
{
if (options == null)
{
return;
}
AllowOnlyAlphanumericNames = options.AllowOnlyAlphanumericNames;
RequireUniqueEmail = options.RequireUniqueEmail;
}
}
}

View File

@ -33,24 +33,6 @@ namespace Microsoft.AspNet.Identity
/// <typeparam name="TUser"></typeparam>
public class UserValidator<TUser> : IUserValidator<TUser> where TUser : class
{
/// <summary>
/// Constructor
/// </summary>
public UserValidator()
{
AllowOnlyAlphanumericUserNames = true;
}
/// <summary>
/// Only allow [A-Za-z0-9@_] in UserNames
/// </summary>
public bool AllowOnlyAlphanumericUserNames { get; set; }
/// <summary>
/// If set, enforces that emails are non empty, valid, and unique
/// </summary>
public bool RequireUniqueEmail { get; set; }
/// <summary>
/// Validates a user before saving
/// </summary>
@ -70,7 +52,7 @@ namespace Microsoft.AspNet.Identity
}
var errors = new List<string>();
await ValidateUserName(manager, user, errors);
if (RequireUniqueEmail)
if (manager.Options.User.RequireUniqueEmail)
{
await ValidateEmail(manager, user, errors);
}
@ -126,7 +108,7 @@ namespace Microsoft.AspNet.Identity
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "UserName"));
}
else if (AllowOnlyAlphanumericUserNames && !userName.All(IsAlphaNumeric))
else if (manager.Options.User.AllowOnlyAlphanumericNames && !userName.All(IsAlphaNumeric))
{
// If any characters are not letters or digits, its an illegal user name
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidUserName, userName));

View File

@ -3,9 +3,9 @@
"dependencies": {
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
"System.Security.Claims" : "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
"Microsoft.AspNet.Security.DataProtection" : "0.1-alpha-*"
"Microsoft.AspNet.Security.DataProtection" : "0.1-alpha-*",
"System.Security.Claims" : "0.1-alpha-*"
},
"configurations": {
"net45": {},
@ -20,6 +20,7 @@
"System.Linq.Expressions": "4.0.0.0",
"System.Linq.Queryable": "4.0.0.0",
"System.Reflection": "4.0.10.0",
"System.Reflection.Extensions": "4.0.0.0",
"System.Resources.ResourceManager": "4.0.0.0",
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",

View File

@ -26,5 +26,8 @@
<Compile Include="TestIdentityFactory.cs" />
<Compile Include="UserStoreTest.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -17,12 +17,64 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.AspNet.Identity.Test;
using Microsoft.Data.Entity;
using Microsoft.Data.InMemory;
using Xunit;
namespace Microsoft.AspNet.Identity.Entity.Test
{
public class RoleStoreTest
{
class ApplicationRoleManager : RoleManager<EntityRole>
{
public ApplicationRoleManager(IServiceProvider services, IRoleStore<EntityRole> store) : base(services, store) { }
}
[Fact]
public async Task CanCreateUsingAddRoleManager()
{
var services = new ServiceCollection();
#if NET45
// services.AddEntityFramework(s => s.AddSqlServer());
//#else
services.AddEntityFramework(s => s.AddInMemoryStore());
#endif
// TODO: this should construct a new instance of InMemoryStore
var store = new RoleStore<EntityRole>(new IdentityContext());
services.AddIdentity<EntityUser, EntityRole>(s =>
{
s.AddRoleStore(() => store);
s.AddRoleManager<ApplicationRoleManager>();
});
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationRoleManager>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityRole("arole")));
}
[Fact]
public async Task CanCreateRoleWithSingletonManager()
{
var services = new ServiceCollection();
#if NET45
// services.AddEntityFramework(s => s.AddSqlServer());
//#else
services.AddEntityFramework(s => s.AddInMemoryStore());
#endif
services.AddTransient<DbContext, IdentityContext>();
services.AddTransient<IRoleStore<EntityRole>, RoleStore<EntityRole>>();
//todo: services.AddSingleton<RoleManager<EntityRole>, RoleManager<EntityRole>>();
// TODO: How to configure SqlServer?
services.AddSingleton<ApplicationRoleManager, ApplicationRoleManager>();
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationRoleManager>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityRole("someRole")));
}
[Fact]
public async Task RoleStoreMethodsThrowWhenDisposedTest()
{

View File

@ -0,0 +1,121 @@
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.AspNet.Identity.Entity;
using Microsoft.AspNet.Identity.Test;
using Microsoft.AspNet.PipelineCore;
using System;
using System.Threading.Tasks;
using Microsoft.Data.Entity;
using Xunit;
namespace Microsoft.AspNet.Identity.Entity.Test
{
public class StartupTest
{
public class ApplicationUser : EntityUser { }
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IServiceProvider services, IUserStore<ApplicationUser> store, IOptionsAccessor<IdentityOptions> options) : base(services, store, options) { }
}
public class ApplicationRoleManager : RoleManager<EntityRole>
{
public ApplicationRoleManager(IServiceProvider services, IRoleStore<EntityRole> store) : base(services, store) { }
}
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
{
public int Order { get { return 0; } }
public void Setup(IdentityOptions options)
{
options.Password.RequiredLength = -1;
}
}
[Fact]
public void CanCustomizeIdentityOptions()
{
IBuilder builder = new Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services => {
services.AddIdentity<IdentityUser>(identityServices => { });
services.AddSetup<PasswordsNegativeLengthSetup>();
});
var setup = builder.ApplicationServices.GetService<IOptionsSetup<IdentityOptions>>();
Assert.IsType(typeof(PasswordsNegativeLengthSetup), setup);
var optionsGetter = builder.ApplicationServices.GetService<IOptionsAccessor<IdentityOptions>>();
Assert.NotNull(optionsGetter);
setup.Setup(optionsGetter.Options);
var myOptions = optionsGetter.Options;
Assert.True(myOptions.Password.RequireLowercase);
Assert.True(myOptions.Password.RequireDigit);
Assert.True(myOptions.Password.RequireNonLetterOrDigit);
Assert.True(myOptions.Password.RequireUppercase);
Assert.Equal(-1, myOptions.Password.RequiredLength);
}
[Fact]
public void CanSetupIdentityOptions()
{
IBuilder app = new Builder(new ServiceCollection().BuildServiceProvider());
app.UseServices(services => services.AddIdentity<IdentityUser>(identityServices => identityServices.SetupOptions(options => options.User.RequireUniqueEmail = true)));
var optionsGetter = app.ApplicationServices.GetService<IOptionsAccessor<IdentityOptions>>();
Assert.NotNull(optionsGetter);
var myOptions = optionsGetter.Options;
Assert.True(myOptions.User.RequireUniqueEmail);
}
[Fact]
public async Task EnsureStartupUsageWorks()
{
IBuilder builder = new Builder(new ServiceCollection().BuildServiceProvider());
//builder.UseServices(services => services.AddIdentity<ApplicationUser>(s =>
// s.AddEntity<ApplicationDbContext>()
//{
builder.UseServices(services =>
{
services.AddEntityFramework();
services.AddTransient<DbContext, IdentityContext>();
services.AddIdentity<ApplicationUser, EntityRole>(s =>
{
s.AddEntity();
s.AddUserManager<ApplicationUserManager>();
s.AddRoleManager<ApplicationRoleManager>();
});
});
var userStore = builder.ApplicationServices.GetService<IUserStore<ApplicationUser>>();
var roleStore = builder.ApplicationServices.GetService<IRoleStore<EntityRole>>();
var userManager = builder.ApplicationServices.GetService<ApplicationUserManager>();
//TODO: var userManager = builder.ApplicationServices.GetService<UserManager<IdentityUser>();
var roleManager = builder.ApplicationServices.GetService<ApplicationRoleManager>();
Assert.NotNull(userStore);
Assert.NotNull(userManager);
Assert.NotNull(roleStore);
Assert.NotNull(roleManager);
//await CreateAdminUser(builder.ApplicationServices);
}
private static async Task CreateAdminUser(IServiceProvider serviceProvider)
{
const string userName = "admin";
const string roleName = "Admins";
const string password = "1qaz@WSX";
var userManager = serviceProvider.GetService<ApplicationUserManager>();
var roleManager = serviceProvider.GetService<ApplicationRoleManager>();
var user = new ApplicationUser { UserName = userName };
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(new EntityRole { Name = roleName }));
IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, roleName));
}
}
}

View File

@ -36,74 +36,63 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public static DbContext CreateContext()
{
var serviceProvider = new ServiceCollection()
.AddEntityFramework(s => s.AddInMemoryStore())
.BuildServiceProvider();
//#if NET45
// .AddEntityFramework(s => s.AddSqlServer())
//#else
.AddEntityFramework(s => s.AddInMemoryStore())
//#endif
.BuildServiceProvider();
var db = new IdentityContext(serviceProvider);
// var sql = db.Configuration.DataStore as SqlServerDataStore;
// if (sql != null)
// {
//#if NET45
// var builder = new DbConnectionStringBuilder {ConnectionString = sql.ConnectionString};
// var targetDatabase = builder["Database"].ToString();
// // Connect to master, check if database exists, and create if not
// builder.Add("Database", "master");
// using (var masterConnection = new SqlConnection(builder.ConnectionString))
// {
// masterConnection.Open();
// var masterCommand = masterConnection.CreateCommand();
// masterCommand.CommandText = "SELECT COUNT(*) FROM sys.databases WHERE [name]=N'" + targetDatabase +
// "'";
// if ((int?) masterCommand.ExecuteScalar() < 1)
// {
// masterCommand.CommandText = "CREATE DATABASE [" + targetDatabase + "]";
// masterCommand.ExecuteNonQuery();
// using (var conn = new SqlConnection(sql.ConnectionString))
// {
// conn.Open();
// var command = conn.CreateCommand();
// command.CommandText = @"
//CREATE TABLE [dbo].[AspNetUsers] (
//[Id] NVARCHAR (128) NOT NULL,
//[Email] NVARCHAR (256) NULL,
//[EmailConfirmed] BIT NOT NULL,
//[PasswordHash] NVARCHAR (MAX) NULL,
//[SecurityStamp] NVARCHAR (MAX) NULL,
//[PhoneNumber] NVARCHAR (MAX) NULL,
//[PhoneNumberConfirmed] BIT NOT NULL,
//[TwoFactorEnabled] BIT NOT NULL,
//[LockoutEndDateUtc] DATETIME NULL,
//[LockoutEnabled] BIT NOT NULL,
//[AccessFailedCount] INT NOT NULL,
//[UserName] NVARCHAR (256) NOT NULL
//) ";
// //CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
// command.ExecuteNonQuery();
// }
// }
// }
//#else
// throw new NotSupportedException("SQL Server is not yet supported when running against K10.");
//#endif
// TODO: Recreate DB, doesn't support String ID or Identity context yet
//if (!db.Database.Exists())
//{
// db.Database.Create();
//}
// TODO: CreateAsync DB?
return db;
}
public class TestSetup : IOptionsSetup<IdentityOptions>
{
private readonly IdentityOptions _options;
public TestSetup(IdentityOptions options)
{
_options = options;
}
public int Order { get { return 0; } }
public void Setup(IdentityOptions options)
{
options.Copy(_options);
}
}
public static UserManager<EntityUser> CreateManager(DbContext context)
{
var manager = new UserManager<EntityUser>(new UserStore(context))
var services = new ServiceCollection();
services.AddTransient<IUserValidator<EntityUser>, UserValidator<EntityUser>>();
services.AddTransient<IPasswordValidator<IdentityUser>, PasswordValidator<IdentityUser>>();
services.AddInstance<IUserStore<EntityUser>>(new UserStore<EntityUser>(context));
services.AddSingleton<UserManager<EntityUser>, UserManager<EntityUser>>();
var options = new IdentityOptions
{
UserValidator = new UserValidator<EntityUser>(),
PasswordValidator = new PasswordValidator()
Password = new PasswordOptions
{
RequireDigit = false,
RequireLowercase = false,
RequireNonLetterOrDigit = false,
RequireUppercase = false
}
};
return manager;
var optionsAccessor = new OptionsAccessor<IdentityOptions>(new[] { new TestSetup(options) });
//services.AddInstance<IOptionsAccessor<IdentityOptions>>(new OptionsAccessor<IdentityOptions>(new[] { new TestSetup(options) }));
//return services.BuildServiceProvider().GetService<UserManager<EntityUser>>();
return new UserManager<EntityUser>(services.BuildServiceProvider(), new UserStore<EntityUser>(context), optionsAccessor);
}
public static UserManager<EntityUser> CreateManager()
@ -113,7 +102,11 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public static RoleManager<EntityRole> CreateRoleManager(DbContext context)
{
return new RoleManager<EntityRole>(new RoleStore<EntityRole, string>(context));
var services = new ServiceCollection();
services.AddTransient<IRoleValidator<EntityRole>, RoleValidator<EntityRole>>();
services.AddInstance<IRoleStore<EntityRole>>(new RoleStore<EntityRole, string>(context));
// return services.BuildServiceProvider().GetService<RoleManager<EntityRole>>();
return new RoleManager<EntityRole>(services.BuildServiceProvider(), new RoleStore<EntityRole, string>(context));
}
public static RoleManager<EntityRole> CreateRoleManager()

View File

@ -17,11 +17,12 @@
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.AspNet.Identity.Test;
using Microsoft.AspNet.Testing;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Storage;
using Microsoft.Data.InMemory;
using Microsoft.Data.SqlServer;
using System;
using System.Linq;
using System.Security.Claims;
@ -35,37 +36,49 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
class ApplicationUserManager : UserManager<EntityUser>
{
public ApplicationUserManager(IServiceProvider services) : base(services) { }
public ApplicationUserManager(IServiceProvider services, IUserStore<EntityUser> store, IOptionsAccessor<IdentityOptions> options) : base(services, store, options) { }
}
// [Fact] Disabled - see issue #48
[Fact]
public async Task CanUseAddedManagerInstance()
{
var services = new ServiceCollection();
var store = new UserStore(new IdentityContext());
services.AddInstance<UserManager<EntityUser>>(new UserManager<EntityUser>(store));
#if NET45
// services.AddEntityFramework(s => s.AddSqlServer());
//#else
services.AddEntityFramework(s => s.AddInMemoryStore());
#endif
services.AddSingleton<IOptionsAccessor<IdentityOptions>, OptionsAccessor<IdentityOptions>>();
services.AddInstance<DbContext>(new IdentityContext());
services.AddTransient<IUserStore<EntityUser>, UserStore>();
services.AddSingleton<ApplicationUserManager, ApplicationUserManager>();
var provider = services.BuildServiceProvider();
var manager = provider.GetService<UserManager<EntityUser>>();
var manager = provider.GetService<ApplicationUserManager>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello")));
}
[Fact]
public async Task CanUseSingletonManagerInstance()
public async Task CanCreateUsingAddUserManager()
{
var services = new ServiceCollection();
#if NET45
// services.AddEntityFramework(s => s.AddSqlServer());
//#else
services.AddEntityFramework(s => s.AddInMemoryStore());
#endif
// TODO: this needs to construct a new instance of InMemoryStore
var store = new UserStore(new IdentityContext());
services.AddIdentity<EntityUser, EntityRole>(s =>
{
s.UseUserStore(() => store);
s.UseUserManager<ApplicationUserManager>();
s.AddUserStore(() => store);
s.AddUserManager<ApplicationUserManager>();
});
//services.AddSingleton<ApplicationUserManager, ApplicationUserManager>();
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationUserManager>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello")));
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello2")));
}
//[Fact]
@ -146,30 +159,30 @@ namespace Microsoft.AspNet.Identity.Entity.Test
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",
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await store.SetSecurityStampAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("claim",
await Assert.ThrowsAsync<ArgumentNullException>("claim",
async () => await store.AddClaimAsync(new EntityUser("fake"), null));
await Assert.ThrowsAsync<ArgumentNullException>("claim",
await Assert.ThrowsAsync<ArgumentNullException>("claim",
async () => await store.RemoveClaimAsync(new EntityUser("fake"), null));
await Assert.ThrowsAsync<ArgumentNullException>("login",
await Assert.ThrowsAsync<ArgumentNullException>("login",
async () => await store.AddLoginAsync(new EntityUser("fake"), null));
await Assert.ThrowsAsync<ArgumentNullException>("login",
await Assert.ThrowsAsync<ArgumentNullException>("login",
async () => await store.RemoveLoginAsync(new EntityUser("fake"), null));
await Assert.ThrowsAsync<ArgumentNullException>("login", async () => await store.FindByLoginAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetEmailConfirmedAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
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",
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await store.GetPhoneNumberConfirmedAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
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",
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));
@ -308,7 +321,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
var manager = TestIdentityFactory.CreateManager();
var user = new EntityUser("UpdateBlocked") { Email = email };
manager.UserValidator = new UserValidator<EntityUser> { RequireUniqueEmail = true };
manager.Options.User.RequireUniqueEmail = true;
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty.");
}
@ -320,7 +333,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
var manager = TestIdentityFactory.CreateManager();
var user = new EntityUser("UpdateBlocked") { Email = email };
manager.UserValidator = new UserValidator<EntityUser> { RequireUniqueEmail = true };
manager.Options.User.RequireUniqueEmail = true;
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid.");
}
#endif
@ -523,7 +536,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task AddDupeEmailFallsWhenUniqueEmailRequired()
{
var manager = TestIdentityFactory.CreateManager();
manager.UserValidator = new UserValidator<EntityUser> { RequireUniqueEmail = true };
manager.Options.User.RequireUniqueEmail = true;
var user = new EntityUser("dupe") { Email = "yup@yup.com" };
var user2 = new EntityUser("dupeEmail") { Email = "yup@yup.com" };
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
@ -618,10 +631,10 @@ namespace Microsoft.AspNet.Identity.Entity.Test
var claims = identity.Claims.ToList();
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"));
claims.Any(c => c.Type == manager.Options.ClaimType.UserName && c.Value == user.UserName));
Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.UserId && c.Value == user.Id.ToString()));
Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Admin"));
Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Local"));
foreach (var cl in userClaims)
{
Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));
@ -790,11 +803,9 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task SingleFailureLockout()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
DefaultAccountLockoutTimeSpan = TimeSpan.FromHours(1),
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
mgr.Options.Lockout.EnabledByDefault = true;
mgr.Options.Lockout.MaxFailedAccessAttempts = 0;
var user = new EntityUser("fastLockout");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -810,12 +821,9 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task TwoFailureLockout()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
DefaultAccountLockoutTimeSpan = TimeSpan.FromHours(1),
UserLockoutEnabledByDefault = true,
MaxFailedAccessAttemptsBeforeLockout = 2
};
mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
mgr.Options.Lockout.EnabledByDefault = true;
mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
var user = new EntityUser("twoFailureLockout");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -835,12 +843,9 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task ResetAccessCountPreventsLockout()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
DefaultAccountLockoutTimeSpan = TimeSpan.FromHours(1),
UserLockoutEnabledByDefault = true,
MaxFailedAccessAttemptsBeforeLockout = 2
};
mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
mgr.Options.Lockout.EnabledByDefault = true;
mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
var user = new EntityUser("resetLockout");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -864,11 +869,8 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task CanEnableLockoutManuallyAndLockout()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
DefaultAccountLockoutTimeSpan = TimeSpan.FromHours(1),
MaxFailedAccessAttemptsBeforeLockout = 2
};
mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1);
mgr.Options.Lockout.MaxFailedAccessAttempts = 2;
var user = new EntityUser("manualLockout");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.False(await mgr.GetLockoutEnabledAsync(user));
@ -891,10 +893,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.EnabledByDefault = true;
var user = new EntityUser("LockoutTest");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -922,10 +921,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.EnabledByDefault = true;
var user = new EntityUser("LockoutUtcNowTest") { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) };
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -937,10 +933,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.EnabledByDefault = true;
var user = new EntityUser("LockoutUtcNowTest");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -953,10 +946,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.EnabledByDefault = true;
var user = new EntityUser("LockoutUtcNowTest") { LockoutEnd = DateTime.UtcNow.AddMinutes(5) };
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -968,10 +958,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task UserLockedOutWithDateTimeLocalKindNowPlus30()
{
var mgr = TestIdentityFactory.CreateManager();
mgr.LockoutPolicy = new LockoutPolicy
{
UserLockoutEnabledByDefault = true,
};
mgr.Options.Lockout.EnabledByDefault = true;
var user = new EntityUser("LockoutTest");
IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
@ -995,11 +982,11 @@ namespace Microsoft.AspNet.Identity.Entity.Test
}
private class AlwaysBadValidator : IUserValidator<EntityUser>, IRoleValidator<EntityRole>,
IPasswordValidator
IPasswordValidator<EntityUser>
{
public const string ErrorMessage = "I'm Bad.";
public Task<IdentityResult> ValidateAsync(string password, CancellationToken token)
public Task<IdentityResult> ValidateAsync(string password, UserManager<EntityUser> manager,CancellationToken token)
{
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
}
@ -1326,7 +1313,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
var manager = TestIdentityFactory.CreateManager();
const string userName = "PhoneTest";
var user = new EntityUser(userName) {PhoneNumber = "123-456-7890"};
var user = new EntityUser(userName) { PhoneNumber = "123-456-7890" };
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
var stamp = await manager.GetSecurityStampAsync(user);
Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890");

View File

@ -1,9 +1,9 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.Bcl.Immutable": "1.1.18-beta-*",
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection": "0.1-alpha-*",
"Microsoft.AspNet.Identity": "",
"Microsoft.AspNet.Identity.Entity": "",
"Microsoft.AspNet.PipelineCore": "0.1-alpha-*",
@ -11,9 +11,12 @@
"Microsoft.AspNet.RequestContainer": "0.1-alpha-*",
"Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*",
"Microsoft.AspNet.Testing": "0.1-alpha-*",
"Microsoft.Bcl.Immutable": "1.1.18-beta-*",
"Microsoft.Data.Entity": "0.1-alpha-*",
"Microsoft.Data.InMemory": "0.1-alpha-*",
"Microsoft.Data.Migrations": "0.1-alpha-*",
"Microsoft.Data.Relational": "0.1-alpha-*",
"Microsoft.Data.SqlServer": "0.1-alpha-*",
"Remotion.Linq": "1.15.13.0",
"System.Security.Claims": "0.1-alpha-*",
"Xunit.KRunner": "0.1-alpha-*",
@ -22,9 +25,11 @@
"xunit.core": "2.0.0-aspnet-*",
"xunit.execution": "2.0.0-aspnet-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"configurations": {
"net45": {
"dependencies": {
"Moq" : "4.2.1312.1622",
"System.Runtime": "",
"System.Collections": "",
"System.Data": ""

View File

@ -23,6 +23,10 @@
<ItemGroup>
<Compile Include="IdentityResultAssert.cs" />
<Compile Include="InMemoryStoreTest.cs" />
<Compile Include="StartupTest.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -18,6 +18,7 @@
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.AspNet.Identity.Test;
using Microsoft.AspNet.PipelineCore;
using System;
using System.Threading.Tasks;
@ -27,23 +28,78 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
{
public class StartupTest
{
public class ApplicationUser : IdentityUser { }
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IServiceProvider services, IUserStore<ApplicationUser> store, IOptionsAccessor<IdentityOptions> options) : base(services, store, options) { }
}
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
{
public int Order { get { return 0; } }
public void Setup(IdentityOptions options)
{
options.Password.RequiredLength = -1;
}
}
[Fact]
public void CanCustomizeIdentityOptions()
{
IBuilder builder = new Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services => {
services.AddIdentity<IdentityUser>(identityServices => { });
services.AddSetup<PasswordsNegativeLengthSetup>();
});
var setup = builder.ApplicationServices.GetService<IOptionsSetup<IdentityOptions>>();
Assert.IsType(typeof(PasswordsNegativeLengthSetup), setup);
var optionsGetter = builder.ApplicationServices.GetService<IOptionsAccessor<IdentityOptions>>();
Assert.NotNull(optionsGetter);
setup.Setup(optionsGetter.Options);
var myOptions = optionsGetter.Options;
Assert.True(myOptions.Password.RequireLowercase);
Assert.True(myOptions.Password.RequireDigit);
Assert.True(myOptions.Password.RequireNonLetterOrDigit);
Assert.True(myOptions.Password.RequireUppercase);
Assert.Equal(-1, myOptions.Password.RequiredLength);
}
[Fact]
public void CanSetupIdentityOptions()
{
IBuilder app = new Builder(new ServiceCollection().BuildServiceProvider());
app.UseServices(services => services.AddIdentity<IdentityUser>(identityServices => identityServices.SetupOptions(options => options.User.RequireUniqueEmail = true)));
var optionsGetter = app.ApplicationServices.GetService<IOptionsAccessor<IdentityOptions>>();
Assert.NotNull(optionsGetter);
var myOptions = optionsGetter.Options;
Assert.True(myOptions.User.RequireUniqueEmail);
}
[Fact]
public async Task EnsureStartupUsageWorks()
{
IBuilder builder = new Builder(new ServiceCollection().BuildServiceProvider());
//builder.UseServices(services => services.AddIdentity<ApplicationUser>(s =>
// s.AddEntity<ApplicationDbContext>()
//{
builder.UseServices(services => services.AddIdentity<ApplicationUser>(s =>
{
s.UseUserStore(() => new InMemoryUserStore<ApplicationUser>());
s.UseUserManager<ApplicationUserManager>();
s.UseRoleStore(() => new InMemoryRoleStore<IdentityRole>());
s.UseRoleManager<ApplicationRoleManager>();
s.AddInMemory();
s.AddUserManager<ApplicationUserManager>();
s.AddRoleManager<ApplicationRoleManager>();
}));
var userStore = builder.ApplicationServices.GetService<IUserStore<ApplicationUser>>();
var roleStore = builder.ApplicationServices.GetService<IRoleStore<IdentityRole>>();
var userManager = builder.ApplicationServices.GetService<ApplicationUserManager>();
//TODO: var userManager = builder.ApplicationServices.GetService<UserManager<IdentityUser>();
var roleManager = builder.ApplicationServices.GetService<ApplicationRoleManager>();
Assert.NotNull(userStore);
@ -67,21 +123,5 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(new IdentityRole { Name = roleName }));
IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, roleName));
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IServiceProvider services) : base(services) { }
}
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IServiceProvider services) : base(services) { }
}
public class ApplicationUser : IdentityUser
{
}
}
}

View File

@ -2,7 +2,7 @@
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
"Microsoft.AspNet.Identity" : "0.1-alpha-*",
"Microsoft.AspNet.Identity.InMemory" : "0.1-alpha-*",
@ -15,9 +15,11 @@
"xunit.core": "2.0.0-aspnet-*",
"xunit.execution": "2.0.0-aspnet-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"configurations": {
"net45": {
"dependencies": {
"Moq" : "4.2.1312.1622",
"System.Runtime": "",
"System.Collections": ""
}

View File

@ -21,8 +21,12 @@
<Content Include="Project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="IdentityExtensionsTest.cs" />
<Compile Include="SignInManagerTest.cs" />
<Compile Include="TestUser.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -15,9 +15,18 @@
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
using System;
using System.Threading;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Abstractions.Security;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Microsoft.AspNet.Identity.InMemory;
using Microsoft.AspNet.Identity.Test;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.Logging;
using Microsoft.AspNet.PipelineCore;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.Security.Cookies;
using Moq;
using System.Security.Claims;
using System.Threading.Tasks;
@ -27,16 +36,117 @@ namespace Microsoft.AspNet.Identity.Security.Test
{
public class SignInManagerTest
{
#if NET45
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task VerifyAccountControllerSignIn(bool isPersistent)
{
IBuilder app = new Builder(new ServiceCollection().BuildServiceProvider());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.IsAny<ClaimsIdentity>(), It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
app.UseServices(services =>
{
services.AddInstance(contextAccessor.Object);
services.AddInstance<ILoggerFactory>(new NullLoggerFactory());
services.AddIdentity<ApplicationUser, IdentityRole>(s =>
{
s.AddInMemory();
s.AddUserManager<ApplicationUserManager>();
s.AddRoleManager<ApplicationRoleManager>();
});
services.AddTransient<ApplicationSignInManager>();
});
// Act
var user = new ApplicationUser
{
UserName = "Yolo"
};
const string password = "Yol0Sw@g!";
var userManager = app.ApplicationServices.GetService<ApplicationUserManager>();
var signInManager = app.ApplicationServices.GetService<ApplicationSignInManager>();
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
var result = await signInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
//[Theory]
//[InlineData(true)]
//[InlineData(false)]
//public async Task VerifyAccountControllerSignInFunctional(bool isPersistent)
//{
// IBuilder app = new Builder(new ServiceCollection().BuildServiceProvider());
// app.UseCookieAuthentication(new CookieAuthenticationOptions
// {
// AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
// });
// TODO: how to functionally test context?
// var context = new DefaultHttpContext(new FeatureCollection());
// var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
// contextAccessor.Setup(a => a.Value).Returns(context);
// app.UseServices(services =>
// {
// services.AddInstance(contextAccessor.Object);
// services.AddInstance<ILoggerFactory>(new NullLoggerFactory());
// services.AddIdentity<ApplicationUser, IdentityRole>(s =>
// {
// s.AddUserStore(() => new InMemoryUserStore<ApplicationUser>());
// s.AddUserManager<ApplicationUserManager>();
// s.AddRoleStore(() => new InMemoryRoleStore<IdentityRole>());
// s.AddRoleManager<ApplicationRoleManager>();
// });
// services.AddTransient<ApplicationSignInManager>();
// });
// // Act
// var user = new ApplicationUser
// {
// UserName = "Yolo"
// };
// const string password = "Yol0Sw@g!";
// var userManager = app.ApplicationServices.GetService<ApplicationUserManager>();
// var signInManager = app.ApplicationServices.GetService<ApplicationSignInManager>();
// IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
// var result = await signInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
// // Assert
// Assert.Equal(SignInStatus.Success, result);
// contextAccessor.VerifyAll();
//}
[Fact]
public void ConstructorNullChecks()
{
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null));
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, null));
}
//TODO: Mock fails in K (this works fine in net45)
[Fact]
public async Task EnsureClaimsIdentityFactoryCreateIdentityCalled()
{
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var userManager = new UserManager<TestUser>(store.Object);
var userManager = MockHelpers.TestUserManager<TestUser>();
var identityFactory = new Mock<IClaimsIdentityFactory<TestUser>>();
const string authType = "Test";
var testIdentity = new ClaimsIdentity(authType);
@ -46,13 +156,21 @@ namespace Microsoft.AspNet.Identity.Security.Test
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 helper = new SignInManager<TestUser> { UserManager = userManager, AuthenticationType = authType, Context = context.Object };
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(userManager, contextAccessor.Object)
{
AuthenticationType = authType
};
// Act
await helper.SignInAsync(user, false, false);
// Assert
identityFactory.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
response.VerifyAll();
}
[Fact]
@ -60,10 +178,13 @@ namespace Microsoft.AspNet.Identity.Security.Test
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = new Mock<UserManager<TestUser>>();
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
var helper = new SignInManager<TestUser> { UserManager = manager.Object };
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
@ -72,13 +193,15 @@ namespace Microsoft.AspNet.Identity.Security.Test
Assert.Equal(SignInStatus.LockedOut, result);
manager.VerifyAll();
}
[Fact]
public async Task CanPasswordSignIn()
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task CanPasswordSignIn(bool isPersistent)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = new Mock<UserManager<TestUser>>();
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
@ -86,28 +209,39 @@ namespace Microsoft.AspNet.Identity.Security.Test
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.IsAny<ClaimsIdentity>(), It.IsAny<AuthenticationProperties>())).Verifiable();
var helper = new SignInManager<TestUser> { UserManager = manager.Object, Context = context.Object };
response.Setup(r => r.SignIn(It.IsAny<ClaimsIdentity>(), It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Theory]
[InlineData(DefaultAuthenticationTypes.ApplicationCookie)]
[InlineData("Microsoft.AspNet.Identity.Security.Application")]
[InlineData("Foo")]
public void SignOutCallsContextResponseSignOut(string authenticationType)
{
// Setup
var manager = MockHelpers.MockUserManager<TestUser>();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignOut(authenticationType)).Verifiable();
var helper = new SignInManager<TestUser> { Context = context.Object, AuthenticationType = authenticationType };
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object)
{
AuthenticationType = authenticationType
};
// Act
helper.SignOut();
@ -122,12 +256,14 @@ namespace Microsoft.AspNet.Identity.Security.Test
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = new Mock<UserManager<TestUser>>();
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable();
var helper = new SignInManager<TestUser> { UserManager = manager.Object };
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
@ -141,9 +277,12 @@ namespace Microsoft.AspNet.Identity.Security.Test
public async Task PasswordSignInFailsWithUnknownUser()
{
// Setup
var manager = new Mock<UserManager<TestUser>>();
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.FindByNameAsync("bogus", CancellationToken.None)).ReturnsAsync(null).Verifiable();
var helper = new SignInManager<TestUser> { UserManager = manager.Object };
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object);
// Act
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
@ -153,60 +292,12 @@ namespace Microsoft.AspNet.Identity.Security.Test
manager.VerifyAll();
}
[Fact]
public async Task PasswordSignInFailsWithNoUserManager()
{
// Setup
var helper = new SignInManager<TestUser>();
// Act
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
// Assert
Assert.Equal(SignInStatus.Failure, result);
}
[Fact]
public async Task SignInWithNoContextDoesNotBlowUp()
{
// Setup
var helper = new SignInManager<TestUser>();
// Act
await helper.SignInAsync(null, false, false);
}
[Fact]
public void SignOutWithNoContextDoesNotBlowUp()
{
// Setup
var helper = new SignInManager<TestUser>();
// Act
helper.SignOut();
}
[Fact]
public async Task CreateUserIdentityReturnsNullNoUserManager()
{
// Setup
var user = new TestUser();
var helper = new SignInManager<TestUser>();
// Act
var result = await helper.CreateUserIdentityAsync(user);
// Assert
Assert.Null(result);
}
[Fact]
public async Task PasswordSignInFailsWithWrongPasswordCanAccessFailedAndLockout()
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = new Mock<UserManager<TestUser>>();
var manager = MockHelpers.MockUserManager<TestUser>();
var lockedout = false;
manager.Setup(m => m.AccessFailedAsync(user, CancellationToken.None)).Returns(() =>
{
@ -216,7 +307,10 @@ namespace Microsoft.AspNet.Identity.Security.Test
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).Returns(() => Task.FromResult(lockedout));
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable();
var helper = new SignInManager<TestUser> { UserManager = manager.Object };
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
@ -226,5 +320,30 @@ namespace Microsoft.AspNet.Identity.Security.Test
manager.VerifyAll();
}
#endif
public class ApplicationSignInManager : SignInManager<ApplicationUser>
{
public ApplicationSignInManager(ApplicationUserManager manager, IContextAccessor<HttpContext> contextAccessor)
: base(manager, contextAccessor)
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;
}
}
public class NullLoggerFactory : ILoggerFactory
{
public ILogger Create(string name)
{
return new NullLogger();
}
}
public class NullLogger : ILogger
{
public bool WriteCore(TraceType eventType, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
return false;
}
}
}
}

View File

@ -2,10 +2,18 @@
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*",
"Microsoft.AspNet.Identity" : "",
"Microsoft.AspNet.Identity.Security" : "",
"Microsoft.AspNet.Identity" : "0.1-alpha-*",
"Microsoft.AspNet.Identity.InMemory" : "0.1-alpha-*",
"Microsoft.AspNet.Identity.Security" : "0.1-alpha-*",
"Microsoft.AspNet.Logging" : "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel" : "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore" : "0.1-alpha-*",
"Microsoft.AspNet.RequestContainer" : "0.1-alpha-*",
"Microsoft.AspNet.Security" : "0.1-alpha-*",
"Microsoft.AspNet.Security.Cookies" : "0.1-alpha-*",
"Microsoft.AspNet.Testing" : "0.1-alpha-*",
"System.Security.Claims" : "0.1-alpha-*",
"Xunit.KRunner": "0.1-alpha-*",
@ -14,6 +22,7 @@
"xunit.core": "2.0.0-aspnet-*",
"xunit.execution": "2.0.0-aspnet-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"configurations": {
"net45": {
"dependencies": {

View File

@ -20,6 +20,8 @@ using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Moq;
using Xunit;
@ -31,7 +33,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task CreateIdentityNullChecks()
{
var factory = new ClaimsIdentityFactory<TestUser>();
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.MockUserManager<TestUser>().Object;
await Assert.ThrowsAsync<ArgumentNullException>("manager",
async () => await factory.CreateAsync(null, null, "whatever"));
await Assert.ThrowsAsync<ArgumentNullException>("user",
@ -50,7 +52,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task EnsureClaimsIdentityHasExpectedClaims(bool supportRoles, bool supportClaims)
{
// Setup
var userManager = new Mock<UserManager<TestUser>>();
var userManager = MockHelpers.MockUserManager<TestUser>();
var user = new TestUser { UserName = "Foo" };
userManager.Setup(m => m.SupportsUserRole).Returns(supportRoles);
userManager.Setup(m => m.SupportsUserClaim).Returns(supportClaims);
@ -60,6 +62,7 @@ namespace Microsoft.AspNet.Identity.Test
userManager.Setup(m => m.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(roleClaims);
var userClaims = new[] { new Claim("Whatever", "Value"), new Claim("Whatever2", "Value2") };
userManager.Setup(m => m.GetClaimsAsync(user, CancellationToken.None)).ReturnsAsync(userClaims);
userManager.Object.Options = new IdentityOptions();
const string authType = "Microsoft.AspNet.Identity";
var factory = new ClaimsIdentityFactory<TestUser>();
@ -68,15 +71,16 @@ namespace Microsoft.AspNet.Identity.Test
var identity = await factory.CreateAsync(userManager.Object, user, authType);
// Assert
var manager = userManager.Object;
Assert.NotNull(identity);
Assert.Equal(authType, identity.AuthenticationType);
var claims = identity.Claims.ToList();
Assert.NotNull(claims);
Assert.True(
claims.Any(c => c.Type == factory.UserNameClaimType && c.Value == user.UserName));
Assert.True(claims.Any(c => c.Type == factory.UserIdClaimType && c.Value == user.Id));
Assert.Equal(supportRoles, claims.Any(c => c.Type == factory.RoleClaimType && c.Value == "Admin"));
Assert.Equal(supportRoles, claims.Any(c => c.Type == factory.RoleClaimType && c.Value == "Local"));
claims.Any(c => c.Type == manager.Options.ClaimType.UserName && c.Value == user.UserName));
Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.UserId && c.Value == user.Id));
Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Admin"));
Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Local"));
foreach (var cl in userClaims)
{
Assert.Equal(supportClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Identity.Test
{
var services = new ServiceCollection();
var validator = new UserValidator<IdentityUser>();
services.AddIdentity<IdentityUser>(b => b.UseUserValidator(() => validator));
services.AddIdentity<IdentityUser>(b => b.AddUserValidator(() => validator));
Assert.Equal(validator, services.BuildServiceProvider().GetService<IUserValidator<IdentityUser>>());
}
@ -37,69 +37,47 @@ namespace Microsoft.AspNet.Identity.Test
public void CanSpecifyPasswordValidatorInstance()
{
var services = new ServiceCollection();
var validator = new PasswordValidator();
services.AddIdentity<IdentityUser>(b => b.UsePasswordValidator(() => validator));
Assert.Equal(validator, services.BuildServiceProvider().GetService<IPasswordValidator>());
}
[Fact]
public void CanSpecifyLockoutPolicyInstance()
{
var services = new ServiceCollection();
var policy = new LockoutPolicy();
services.AddIdentity<IdentityUser>(b => b.UseLockoutPolicy(() => policy));
Assert.Equal(policy, services.BuildServiceProvider().GetService<LockoutPolicy>());
var validator = new PasswordValidator<IdentityUser>();
services.AddIdentity<IdentityUser>(b => b.AddPasswordValidator(() => validator));
Assert.Equal(validator, services.BuildServiceProvider().GetService<IPasswordValidator<IdentityUser>>());
}
[Fact]
public void CanSpecifyPasswordHasherInstance()
{
CanOverride<IPasswordHasher, PasswordHasher>();
CanOverride<IPasswordHasher>(new PasswordHasher());
}
[Fact]
public void CanSpecifyClaimsIdentityFactoryInstance()
{
CanOverride<IClaimsIdentityFactory<IdentityUser>, ClaimsIdentityFactory<IdentityUser>>();
CanOverride<IClaimsIdentityFactory<IdentityUser>>(new ClaimsIdentityFactory<IdentityUser>());
}
[Fact]
public void EnsureDefaultServices()
{
var services = new ServiceCollection();
var builder = new IdentityBuilder<IdentityUser, IdentityRole>(services);
builder.UseIdentity();
services.AddIdentity<IdentityUser>(identity => { });
var provider = services.BuildServiceProvider();
var userValidator = provider.GetService<IUserValidator<IdentityUser>>() as UserValidator<IdentityUser>;
Assert.NotNull(userValidator);
Assert.True(userValidator.AllowOnlyAlphanumericUserNames);
Assert.False(userValidator.RequireUniqueEmail);
var pwdValidator = provider.GetService<IPasswordValidator>() as PasswordValidator;
Assert.NotNull(userValidator);
Assert.True(pwdValidator.RequireDigit);
Assert.True(pwdValidator.RequireLowercase);
Assert.True(pwdValidator.RequireNonLetterOrDigit);
Assert.True(pwdValidator.RequireUppercase);
Assert.Equal(6, pwdValidator.RequiredLength);
var pwdValidator = provider.GetService<IPasswordValidator<IdentityUser>>() as PasswordValidator<IdentityUser>;
Assert.NotNull(pwdValidator);
var hasher = provider.GetService<IPasswordHasher>() as PasswordHasher;
Assert.NotNull(hasher);
var claimsFactory = provider.GetService<IClaimsIdentityFactory<IdentityUser>>() as ClaimsIdentityFactory<IdentityUser>;
Assert.NotNull(claimsFactory);
Assert.Equal(ClaimTypes.Role, claimsFactory.RoleClaimType);
Assert.Equal(ClaimsIdentityFactory<IdentityUser>.DefaultSecurityStampClaimType, claimsFactory.SecurityStampClaimType);
Assert.Equal(ClaimTypes.Name, claimsFactory.UserNameClaimType);
Assert.Equal(ClaimTypes.NameIdentifier, claimsFactory.UserIdClaimType);
}
private static void CanOverride<TService, TImplementation>() where TImplementation : TService,new()
private static void CanOverride<TService>(TService instance)
{
var services = new ServiceCollection();
var instance = new TImplementation();
services.AddIdentity<IdentityUser>(b => b.Use<TService>(() => instance));
services.AddIdentity<IdentityUser>(b => b.AddInstance<TService>(() => instance));
Assert.Equal(instance, services.BuildServiceProvider().GetService<TService>());
}

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNet.ConfigurationModel;
using Microsoft.AspNet.ConfigurationModel.Sources;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
{
public class IdentityOptionsTest
{
[Fact]
public void VerifyDefaultOptions()
{
var options = new IdentityOptions();
Assert.False(options.Lockout.EnabledByDefault);
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.RequireNonLetterOrDigit);
Assert.True(options.Password.RequireUppercase);
Assert.Equal(6, options.Password.RequiredLength);
Assert.True(options.User.AllowOnlyAlphanumericNames);
Assert.False(options.User.RequireUniqueEmail);
Assert.Equal(ClaimTypes.Role, options.ClaimType.Role);
Assert.Equal(ClaimTypes.Name, options.ClaimType.UserName);
Assert.Equal(ClaimTypes.NameIdentifier, options.ClaimType.UserId);
Assert.Equal(ClaimTypeOptions.DefaultSecurityStampClaimType, options.ClaimType.SecurityStamp);
}
[Fact]
public void CopyNullIsNoop()
{
var options = new IdentityOptions();
options.Copy(null);
}
[Fact]
public void IdentityOptionsFromConfig()
{
const string roleClaimType = "rolez";
const string usernameClaimType = "namez";
const string useridClaimType = "idz";
const string securityStampClaimType = "stampz";
var dic = new Dictionary<string, string>
{
{"identity:claimtype:role", roleClaimType},
{"identity:claimtype:username", usernameClaimType},
{"identity:claimtype:userid", useridClaimType},
{"identity:claimtype:securitystamp", securityStampClaimType},
{"identity:user:requireUniqueEmail", "true"},
{"identity:password:RequiredLength", "10"},
{"identity:password:RequireNonLetterOrDigit", "false"},
{"identity:password:RequireUpperCase", "false"},
{"identity:password:RequireDigit", "false"},
{"identity:password:RequireLowerCase", "false"}
};
var config = new ConfigurationModel.Configuration { new MemoryConfigurationSource(dic) };
Assert.Equal(roleClaimType, config.Get("identity:claimtype:role"));
var options = new IdentityOptions(config);
Assert.Equal(roleClaimType, options.ClaimType.Role);
Assert.Equal(useridClaimType, options.ClaimType.UserId);
Assert.Equal(usernameClaimType, options.ClaimType.UserName);
Assert.Equal(securityStampClaimType, options.ClaimType.SecurityStamp);
Assert.True(options.User.RequireUniqueEmail);
Assert.True(options.User.AllowOnlyAlphanumericNames);
Assert.True(options.User.AllowOnlyAlphanumericNames);
Assert.False(options.Password.RequireDigit);
Assert.False(options.Password.RequireLowercase);
Assert.False(options.Password.RequireNonLetterOrDigit);
Assert.False(options.Password.RequireUppercase);
Assert.Equal(10, options.Password.RequiredLength);
}
[Fact]
public void ClaimTypeOptionsFromConfig()
{
const string roleClaimType = "rolez";
const string usernameClaimType = "namez";
const string useridClaimType = "idz";
const string securityStampClaimType = "stampz";
var dic = new Dictionary<string, string>
{
{"role", roleClaimType},
{"username", usernameClaimType},
{"userid", useridClaimType},
{"securitystamp", securityStampClaimType}
};
var config = new ConfigurationModel.Configuration {new MemoryConfigurationSource(dic)};
Assert.Equal(roleClaimType, config.Get("role"));
var options = new ClaimTypeOptions(config);
Assert.Equal(roleClaimType, options.Role);
Assert.Equal(useridClaimType, options.UserId);
Assert.Equal(usernameClaimType, options.UserName);
Assert.Equal(securityStampClaimType, options.SecurityStamp);
}
[Fact]
public void PasswordOptionsFromConfig()
{
var dic = new Dictionary<string, string>
{
{"RequiredLength", "10"},
{"RequireNonLetterOrDigit", "false"},
{"RequireUpperCase", "false"},
{"RequireDigit", "false"},
{"RequireLowerCase", "false"}
};
var config = new ConfigurationModel.Configuration { new MemoryConfigurationSource(dic) };
var options = new PasswordOptions(config);
Assert.False(options.RequireDigit);
Assert.False(options.RequireLowercase);
Assert.False(options.RequireNonLetterOrDigit);
Assert.False(options.RequireUppercase);
Assert.Equal(10, options.RequiredLength);
}
}
}

View File

@ -22,6 +22,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ClaimsIdentityFactoryTest.cs" />
<Compile Include="IdentityBuilderTest.cs" />
<Compile Include="IdentityResultAssert.cs" />
<Compile Include="IdentityResultTest.cs" />
<Compile Include="NoopRoleStore.cs" />
@ -30,10 +31,12 @@
<Compile Include="RoleManagerTest.cs" />
<Compile Include="RoleValidatorTest.cs" />
<Compile Include="TestRole.cs" />
<Compile Include="TestServices.cs" />
<Compile Include="TestUser.cs" />
<Compile Include="UserManagerTest.cs" />
<Compile Include="UserValidatorTest.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\ProjectK\Microsoft.Web.ProjectK.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -39,11 +39,12 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ValidateThrowsWithNullTest()
{
// Setup
var validator = new PasswordValidator();
var validator = new PasswordValidator<IdentityUser>();
// Act
// Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => validator.ValidateAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync("foo", null));
}
@ -54,8 +55,13 @@ namespace Microsoft.AspNet.Identity.Test
public async Task FailsIfTooShortTests(string input)
{
const string error = "Passwords must be at least 6 characters.";
var valid = new PasswordValidator {RequiredLength = 6};
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input), error);
var manager = MockHelpers.TestUserManager<IdentityUser>();
var valid = new PasswordValidator<IdentityUser>();
manager.Options.Password.RequireUppercase = false;
manager.Options.Password.RequireNonLetterOrDigit = false;
manager.Options.Password.RequireLowercase = false;
manager.Options.Password.RequireDigit = false;
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager), error);
}
[Theory]
@ -63,8 +69,13 @@ namespace Microsoft.AspNet.Identity.Test
[InlineData("aaaaaaaaaaa")]
public async Task SuccessIfLongEnoughTests(string input)
{
var valid = new PasswordValidator {RequiredLength = 6};
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input));
var manager = MockHelpers.TestUserManager<IdentityUser>();
var valid = new PasswordValidator<IdentityUser>();
manager.Options.Password.RequireUppercase = false;
manager.Options.Password.RequireNonLetterOrDigit = false;
manager.Options.Password.RequireLowercase = false;
manager.Options.Password.RequireDigit = false;
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
}
[Theory]
@ -72,8 +83,14 @@ namespace Microsoft.AspNet.Identity.Test
[InlineData("aaaaaaaaaaa")]
public async Task FailsWithoutRequiredNonAlphanumericTests(string input)
{
var valid = new PasswordValidator {RequireNonLetterOrDigit = true};
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input),
var manager = MockHelpers.TestUserManager<IdentityUser>();
var valid = new PasswordValidator<IdentityUser>();
manager.Options.Password.RequireUppercase = false;
manager.Options.Password.RequireNonLetterOrDigit = true;
manager.Options.Password.RequireLowercase = false;
manager.Options.Password.RequireDigit = false;
manager.Options.Password.RequiredLength = 0;
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager),
"Passwords must have at least one non letter or digit character.");
}
@ -83,8 +100,14 @@ namespace Microsoft.AspNet.Identity.Test
[InlineData("!!!!!!")]
public async Task SucceedsWithRequiredNonAlphanumericTests(string input)
{
var valid = new PasswordValidator {RequireNonLetterOrDigit = true};
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input));
var manager = MockHelpers.TestUserManager<IdentityUser>();
var valid = new PasswordValidator<IdentityUser>();
manager.Options.Password.RequireUppercase = false;
manager.Options.Password.RequireNonLetterOrDigit = true;
manager.Options.Password.RequireLowercase = false;
manager.Options.Password.RequireDigit = false;
manager.Options.Password.RequiredLength = 0;
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
}
[Theory]
@ -101,14 +124,8 @@ namespace Microsoft.AspNet.Identity.Test
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 valid = new PasswordValidator
{
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
RequiredLength = 6
};
var manager = MockHelpers.TestUserManager<IdentityUser>();
var valid = new PasswordValidator<IdentityUser>();
var errors = new List<string>();
if ((errorMask & Errors.Length) != Errors.None)
{
@ -132,11 +149,11 @@ namespace Microsoft.AspNet.Identity.Test
}
if (errors.Count == 0)
{
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input));
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
}
else
{
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input), string.Join(" ", errors));
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager), string.Join(" ", errors));
}
}
}

View File

@ -19,22 +19,18 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
{
public class RoleManagerTest
{
[Fact]
public void ConstructorThrowsWithNullStore()
{
Assert.Throws<ArgumentNullException>("store", () => new RoleManager<TestRole>((IRoleStore<TestRole>)null));
}
[Fact]
public void RolesQueryableFailWhenStoreNotImplemented()
{
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var manager = new RoleManager<TestRole>(new ServiceCollection().BuildServiceProvider(), new NoopRoleStore());
Assert.False(manager.SupportsQueryableRoles);
Assert.Throws<NotSupportedException>(() => manager.Roles.Count());
}
@ -42,7 +38,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public void DisposeAfterDisposeDoesNotThrow()
{
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var manager = new RoleManager<TestRole>(new ServiceCollection().BuildServiceProvider(), new NoopRoleStore());
manager.Dispose();
manager.Dispose();
}
@ -50,10 +46,12 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task RoleManagerPublicNullChecks()
{
var provider = new ServiceCollection().BuildServiceProvider();
Assert.Throws<ArgumentNullException>("store",
() => new RoleManager<TestRole>((IRoleStore<TestRole>)null));
var manager = new RoleManager<TestRole>(new NotImplementedStore());
Assert.Throws<ArgumentNullException>("services", () => manager.Initialize(null));
() => new RoleManager<TestRole>(provider, null));
Assert.Throws<ArgumentNullException>("services",
() => new RoleManager<TestRole>(null, new NotImplementedStore()));
var manager = new RoleManager<TestRole>(provider, 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));
@ -64,7 +62,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task RoleStoreMethodsThrowWhenDisposed()
{
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var manager = new RoleManager<TestRole>(new ServiceCollection().BuildServiceProvider(), new NoopRoleStore());
manager.Dispose();
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.FindByIdAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.FindByNameAsync(null));

View File

@ -17,6 +17,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -27,7 +29,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ValidateThrowsWithNull()
{
// Setup
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var manager = new RoleManager<TestRole>(new ServiceCollection().BuildServiceProvider(), new NoopRoleStore());
var validator = new RoleValidator<TestRole>();
// Act
@ -42,7 +44,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ValidateFailsWithTooShortRoleName(string input)
{
// Setup
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var manager = new RoleManager<TestRole>(new ServiceCollection().BuildServiceProvider(), new NoopRoleStore());
var validator = new RoleValidator<TestRole>();
var user = new TestRole {Name = input};

View File

@ -34,23 +34,22 @@ namespace Microsoft.AspNet.Identity.Test
{
public IUserStore<TestUser> StorePublic { get { return Store; } }
public TestManager(IServiceProvider provider) : base(provider) { }
public TestManager(IServiceProvider provider, IUserStore<TestUser> store, IOptionsAccessor<IdentityOptions> options) : base(provider, store, options) { }
}
[Fact]
public void EnsureDefaultServicesDefaultsWithStoreWorks()
{
var services = new ServiceCollection {IdentityServices.GetDefaultUserServices<TestUser>()};
services.AddInstance<IUserStore<TestUser>>(new NoopUserStore());
var manager = new TestManager(services.BuildServiceProvider());
services.AddInstance<IOptionsAccessor<IdentityOptions>>(new OptionsAccessor<IdentityOptions>(null));
services.AddTransient<IUserStore<TestUser>, NoopUserStore>();
services.AddTransient<TestManager>();
var manager = services.BuildServiceProvider().GetService<TestManager>();
Assert.NotNull(manager.PasswordHasher);
Assert.NotNull(manager.PasswordValidator);
Assert.NotNull(manager.UserValidator);
Assert.NotNull(manager.StorePublic);
Assert.NotNull(manager.LockoutPolicy);
Assert.Equal(TimeSpan.FromMinutes(5), manager.LockoutPolicy.DefaultAccountLockoutTimeSpan);
Assert.Equal(5, manager.LockoutPolicy.MaxFailedAccessAttemptsBeforeLockout);
Assert.False(manager.LockoutPolicy.UserLockoutEnabledByDefault);
Assert.NotNull(manager.Options);
}
#if NET45
@ -61,11 +60,9 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var options = new OptionsAccessor<IdentityOptions>(null);
store.Setup(s => s.CreateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var validator = new Mock<UserValidator<TestUser>>();
var userManager = new UserManager<TestUser>(store.Object);
validator.Setup(v => v.ValidateAsync(userManager, user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
userManager.UserValidator = validator.Object;
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.CreateAsync(user);
@ -81,8 +78,9 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var options = new OptionsAccessor<IdentityOptions>(null);
store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = new UserManager<TestUser>(store.Object);
var userManager = new UserManager<TestUser>(new ServiceCollection().BuildServiceProvider(), store.Object, options);
// Act
var result = await userManager.DeleteAsync(user);
@ -99,10 +97,7 @@ namespace Microsoft.AspNet.Identity.Test
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var validator = new Mock<UserValidator<TestUser>>();
var userManager = new UserManager<TestUser>(store.Object);
validator.Setup(v => v.ValidateAsync(userManager, user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
userManager.UserValidator = validator.Object;
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.UpdateAsync(user);
@ -119,10 +114,7 @@ namespace Microsoft.AspNet.Identity.Test
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser();
store.Setup(s => s.SetUserNameAsync(user, It.IsAny<string>(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var validator = new Mock<UserValidator<TestUser>>();
var userManager = new UserManager<TestUser>(store.Object);
validator.Setup(v => v.ValidateAsync(userManager, user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
userManager.UserValidator = validator.Object;
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.SetUserNameAsync(user, "foo");
@ -139,7 +131,7 @@ namespace Microsoft.AspNet.Identity.Test
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.FindByIdAsync(user.Id, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = new UserManager<TestUser>(store.Object);
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.FindByIdAsync(user.Id);
@ -156,7 +148,7 @@ namespace Microsoft.AspNet.Identity.Test
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser {UserName="Foo"};
store.Setup(s => s.FindByNameAsync(user.UserName, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = new UserManager<TestUser>(store.Object);
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.FindByNameAsync(user.UserName);
@ -184,7 +176,7 @@ namespace Microsoft.AspNet.Identity.Test
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(new List<string>()).Verifiable();
var userManager = new UserManager<TestUser>(store.Object) {UserValidator = null};
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.AddToRolesAsync(user, roles);
@ -200,12 +192,12 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserRoleStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var roles = new string[] { "A", "B", "C" };
var roles = new[] { "A", "B", "C" };
store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(new List<string> { "B" }).Verifiable();
var userManager = new UserManager<TestUser>(store.Object) { UserValidator = null };
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.AddToRolesAsync(user, roles);
@ -221,7 +213,7 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserRoleStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var roles = new string[] { "A", "B", "C" };
var roles = new[] { "A", "B", "C" };
store.Setup(s => s.RemoveFromRoleAsync(user, "A", CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
@ -241,7 +233,7 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.IsInRoleAsync(user, "C", CancellationToken.None))
.Returns(Task.FromResult(true))
.Verifiable();
var userManager = new UserManager<TestUser>(store.Object) { UserValidator = null };
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.RemoveFromRolesAsync(user, roles);
@ -267,7 +259,7 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None))
.Returns(Task.FromResult(false))
.Verifiable();
var userManager = new UserManager<TestUser>(store.Object) { UserValidator = null };
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.RemoveFromRolesAsync(user, roles);
@ -282,21 +274,21 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task CheckPasswordWithNullUserReturnsFalse()
{
var manager = new UserManager<TestUser>(new EmptyStore());
var manager = MockHelpers.TestUserManager(new EmptyStore());
Assert.False(await manager.CheckPasswordAsync(null, "whatevs"));
}
[Fact]
public async Task FindWithUnknownUserAndPasswordReturnsNull()
{
var manager = new UserManager<TestUser>(new EmptyStore());
var manager = MockHelpers.TestUserManager(new EmptyStore());
Assert.Null(await manager.FindByUserNamePasswordAsync("bogus", "whatevs"));
}
[Fact]
public void UsersQueryableFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsQueryableUsers);
Assert.Throws<NotSupportedException>(() => manager.Users.Count());
}
@ -304,7 +296,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task UsersEmailMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserEmail);
await Assert.ThrowsAsync<NotSupportedException>(() => manager.FindByEmailAsync(null));
await Assert.ThrowsAsync<NotSupportedException>(() => manager.SetEmailAsync(null, null));
@ -316,7 +308,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task UsersPhoneNumberMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserPhoneNumber);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.SetPhoneNumberAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.SetPhoneNumberAsync(null, null));
@ -326,7 +318,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task TokenMethodsThrowWithNoTokenProvider()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
await Assert.ThrowsAsync<NotSupportedException>(
async () => await manager.GenerateUserTokenAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(
@ -336,7 +328,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task PasswordMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserPassword);
await Assert.ThrowsAsync<NotSupportedException>(() => manager.CreateAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(() => manager.ChangePasswordAsync(null, null, null));
@ -349,7 +341,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task SecurityStampMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserSecurityStamp);
await Assert.ThrowsAsync<NotSupportedException>(() => manager.UpdateSecurityStampAsync(null));
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetSecurityStampAsync(null));
@ -366,7 +358,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task LoginMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserLogin);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.AddLoginAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.RemoveLoginAsync(null, null));
@ -377,7 +369,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task ClaimMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserClaim);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.AddClaimAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.RemoveClaimAsync(null, null));
@ -387,7 +379,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task TwoFactorStoreMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserTwoFactor);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.GetTwoFactorEnabledAsync(null));
await
@ -397,7 +389,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task LockoutStoreMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserLockout);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.GetLockoutEnabledAsync(null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.SetLockoutEnabledAsync(null, true));
@ -410,7 +402,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task RoleMethodsFailWhenStoreNotImplemented()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserRole);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.AddToRoleAsync(null, "bogus"));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.AddToRolesAsync(null, null));
@ -423,7 +415,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public void DisposeAfterDisposeDoesNotThrow()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
manager.Dispose();
manager.Dispose();
}
@ -432,23 +424,26 @@ namespace Microsoft.AspNet.Identity.Test
public async Task PasswordValidatorBlocksCreate()
{
// TODO: Can switch to Mock eventually
var manager = new UserManager<TestUser>(new EmptyStore())
{
PasswordValidator = new BadPasswordValidtor()
};
var manager = MockHelpers.TestUserManager(new EmptyStore());
manager.PasswordValidator = new BadPasswordValidator<TestUser>();
IdentityResultAssert.IsFailure(await manager.CreateAsync(new TestUser(), "password"),
BadPasswordValidtor.ErrorMessage);
BadPasswordValidator<TestUser>.ErrorMessage);
}
[Fact]
public async Task ManagerPublicNullChecks()
{
var provider = new ServiceCollection().BuildServiceProvider();
Assert.Throws<ArgumentNullException>("serviceProvider",
() => new UserManager<TestUser>((IServiceProvider)null));
var manager = new UserManager<TestUser>(new NotImplementedStore());
Assert.Throws<ArgumentNullException>(() => manager.ClaimsIdentityFactory = null);
Assert.Throws<ArgumentNullException>(() => manager.PasswordHasher = null);
Assert.Throws<ArgumentNullException>("serviceProvider", () => manager.Initialize(null));
() => new UserManager<TestUser>(null, null, null));
Assert.Throws<ArgumentNullException>("store",
() => new UserManager<TestUser>(provider, null, null));
Assert.Throws<ArgumentNullException>("optionsAccessor",
() => new UserManager<TestUser>(provider, new NotImplementedStore(), null));
var manager = new UserManager<TestUser>(provider, new NotImplementedStore(), new OptionsAccessor<IdentityOptions>(null));
Assert.Throws<ArgumentNullException>("value", () => manager.ClaimsIdentityFactory = null);
Assert.Throws<ArgumentNullException>("value", () => manager.PasswordHasher = null);
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
await
Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.CreateIdentityAsync(null, "whatever"));
@ -476,10 +471,8 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task MethodsFailWithUnknownUserTest()
{
var manager = new UserManager<TestUser>(new EmptyStore())
{
UserTokenProvider = new NoOpTokenProvider()
};
var manager = MockHelpers.TestUserManager(new EmptyStore());
manager.UserTokenProvider = new NoOpTokenProvider();
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.GetUserNameAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
@ -583,7 +576,7 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public async Task MethodsThrowWhenDisposedTest()
{
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
manager.Dispose();
Assert.Throws<ObjectDisposedException>(() => manager.ClaimsIdentityFactory);
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.AddClaimAsync(null, null));
@ -620,11 +613,11 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ConfirmEmailAsync(null, null));
}
private class BadPasswordValidtor : IPasswordValidator
private class BadPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
{
public const string ErrorMessage = "I'm Bad.";
public Task<IdentityResult> ValidateAsync(string password, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
}

View File

@ -17,6 +17,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -27,7 +29,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ValidateThrowsWithNull()
{
// Setup
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
var validator = new UserValidator<TestUser>();
// Act
@ -42,7 +44,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ValidateFailsWithTooShortUserNames(string input)
{
// Setup
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
var validator = new UserValidator<TestUser>();
var user = new TestUser {UserName = input};
@ -62,7 +64,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task DefaultAlphaNumericOnlyUserNameValidation(string userName, bool expectSuccess)
{
// Setup
var manager = new UserManager<TestUser>(new NoopUserStore());
var manager = MockHelpers.TestUserManager(new NoopUserStore());
var validator = new UserValidator<TestUser>();
var user = new TestUser {UserName = userName};
@ -89,8 +91,9 @@ namespace Microsoft.AspNet.Identity.Test
public async Task CanAllowNonAlphaNumericUserName(string userName, bool expectSuccess)
{
// Setup
var manager = new UserManager<TestUser>(new NoopUserStore());
var validator = new UserValidator<TestUser> {AllowOnlyAlphanumericUserNames = false};
var manager = MockHelpers.TestUserManager(new NoopUserStore());
manager.Options.User.AllowOnlyAlphanumericNames = false;
var validator = new UserValidator<TestUser>();
var user = new TestUser {UserName = userName};
// Act

View File

@ -11,6 +11,7 @@
"xunit.core": "2.0.0-aspnet-*",
"xunit.execution": "2.0.0-aspnet-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"configurations": {
"net45": {
"dependencies": {

View File

@ -0,0 +1,19 @@
using System;
using Microsoft.AspNet.DependencyInjection;
namespace Microsoft.AspNet.Identity.Test
{
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IServiceProvider services, IUserStore<ApplicationUser> store, IOptionsAccessor<IdentityOptions> options) : base(services, store, options) { }
}
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IServiceProvider services, IRoleStore<IdentityRole> store) : base(services, store) { }
}
public class ApplicationUser : IdentityUser
{
}
}

View File

@ -0,0 +1,33 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.DependencyInjection;
using Microsoft.AspNet.DependencyInjection.Fallback;
using Moq;
namespace Microsoft.AspNet.Identity.Test
{
public static class MockHelpers
{
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var options = new OptionsAccessor<IdentityOptions>(null);
return new Mock<UserManager<TUser>>(new ServiceCollection().BuildServiceProvider(), store.Object, options);
}
public static UserManager<TUser> TestUserManager<TUser>() where TUser : class
{
return TestUserManager(new Mock<IUserStore<TUser>>().Object);
}
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store) where TUser : class
{
var options = new OptionsAccessor<IdentityOptions>(null);
var validator = new Mock<UserValidator<TUser>>();
var userManager = new UserManager<TUser>(new ServiceCollection().BuildServiceProvider(), store, options);
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
userManager.UserValidator = validator.Object;
return userManager;
}
}
}

View File

@ -0,0 +1,17 @@
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.Test
{
public class TestMessageService : IIdentityMessageService
{
public IdentityMessage Message { get; set; }
public Task SendAsync(IdentityMessage message, CancellationToken cancellationToken = default(CancellationToken))
{
Message = message;
return Task.FromResult(0);
}
}
}

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Identity.Test
Id = Guid.NewGuid().ToString();
}
public string Id { get; set; }
public string Id { get; private set; }
public string UserName { get; set; }
}
}

File diff suppressed because it is too large Load Diff