Move SignIn Manger to core

Also cleans up CTP1 interface/tests
This commit is contained in:
Hao Kung 2014-06-04 10:35:07 -07:00
parent 55e1b4312d
commit 9053ec56a4
44 changed files with 772 additions and 783 deletions

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity.Entity;
using Microsoft.Data.Entity;
using Microsoft.Framework.DependencyInjection;
// Move to DI namespace?
namespace Microsoft.AspNet.Identity
{
public static class EntityIdentityBuilderExtensions
{
public static IdentityBuilder<TUser, TRole> AddEntityFrameworkInMemory<TUser, TRole, TDbContext>(this IdentityBuilder<TUser, TRole> builder)
where TUser : EntityUser
where TRole : EntityRole
where TDbContext : DbContext
{
builder.Services.AddScoped<TDbContext>();
builder.Services.AddScoped<IUserStore<TUser>, InMemoryUserStore<TUser, TDbContext>>();
builder.Services.AddScoped<IRoleStore<TRole>, EntityRoleStore<TRole, TDbContext>>();
return builder;
}
// todo: add overloads
public static IdentityBuilder<TUser, IdentityRole> AddEntityFramework<TUser, TContext>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : User where TContext : DbContext
{
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser, TContext>>();
builder.Services.AddScoped<TContext>();
return builder;
}
}
}

View File

@ -10,19 +10,28 @@ using Microsoft.Data.Entity;
namespace Microsoft.AspNet.Identity.Entity
{
public class EntityRoleStore<TRole> : EntityRoleStore<TRole, string> where TRole : EntityRole
public class EntityRoleStore<TRole> : EntityRoleStore<TRole, IdentityContext>
where TRole : EntityRole
{
public EntityRoleStore(DbContext context) : base(context) { }
public EntityRoleStore(IdentityContext context) : base(context) { }
}
public class EntityRoleStore<TRole, TKey> :
public class EntityRoleStore<TRole, TContext> : EntityRoleStore<TRole, string, TContext>
where TRole : EntityRole
where TContext : DbContext
{
public EntityRoleStore(TContext context) : base(context) { }
}
public class EntityRoleStore<TRole, TKey, TContext> :
IQueryableRoleStore<TRole>
where TRole : EntityRole
where TKey : IEquatable<TKey>
where TContext : DbContext
{
private bool _disposed;
public EntityRoleStore(DbContext context)
public EntityRoleStore(TContext context)
{
if (context == null)
{

View File

@ -1,49 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity.Entity;
using Microsoft.Data.Entity;
using Microsoft.Framework.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>, InMemoryUserStore<TUser>>();
builder.Services.AddScoped<IRoleStore<TRole>, EntityRoleStore<TRole>>();
return builder;
}
public static IdentityBuilder<TUser, IdentityRole> AddEntity<TUser>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : User
{
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser>>();
builder.Services.AddScoped<UserManager<TUser>>();
return builder;
}
// todo: remove
public static IdentityBuilder<TUser, IdentityRole> AddEntity<TUser, TContext>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : User where TContext : DbContext
{
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser, TContext>>();
builder.Services.AddScoped<UserManager<TUser>>();
builder.Services.AddScoped<TContext>();
return builder;
}
// todo: add overloads
public static IdentityBuilder<TUser, IdentityRole> AddEntityFramework<TUser, TContext>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : User where TContext : DbContext
{
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser, TContext>>();
builder.Services.AddScoped<UserManager<TUser>>();
builder.Services.AddScoped<TContext>();
return builder;
}
}
}

View File

@ -18,6 +18,14 @@ namespace Microsoft.AspNet.Identity.Entity
public IdentityContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
}
public class IdentityContext<TUser> :
IdentityContext<TUser, EntityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
where TUser : EntityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public IdentityContext() { }
public IdentityContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
}
public class IdentityContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : DbContext
where TUser : EntityUser<TKey, TUserLogin, TUserRole, TUserClaim>
where TRole : EntityRole<TKey, TUserRole>

View File

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Entity;
using Microsoft.Data.Entity;
namespace Microsoft.Framework.DependencyInjection
{
public static class IdentityEntityServiceCollectionExtensions
{
public static IdentityBuilder<TUser, IdentityRole> AddIdentityEntityFramework<TContext, TUser>(this ServiceCollection services)
where TUser : User where TContext : DbContext
{
var builder = services.AddIdentity<TUser, IdentityRole>();
services.AddScoped<TContext>();
services.AddScoped<IUserStore<TUser>, UserStore<TUser, TContext>>();
return builder;
}
}
}

View File

@ -12,6 +12,7 @@ namespace Microsoft.AspNet.Identity.Entity
{
public IdentitySqlContext() { }
public IdentitySqlContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
public IdentitySqlContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider, nameOrConnectionString) { }
public IdentitySqlContext(DbContextOptions options) : base(options) { }
public IdentitySqlContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
}
@ -23,16 +24,23 @@ namespace Microsoft.AspNet.Identity.Entity
public DbSet<IdentityUserClaim> UserClaims { get; set; }
//public DbSet<TRole> Roles { get; set; }
private readonly string _nameOrConnectionString;
public IdentitySqlContext() { }
public IdentitySqlContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider)
{
_nameOrConnectionString = nameOrConnectionString;
}
public IdentitySqlContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
public IdentitySqlContext(DbContextOptions options) : base(options) { }
public IdentitySqlContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
protected override void OnConfiguring(DbContextOptions builder)
{
// TODO: pull connection string from config
builder.UseSqlServer(@"Server=(localdb)\v11.0;Database=SimpleIdentity-5-28;Trusted_Connection=True;");
if (!string.IsNullOrEmpty(_nameOrConnectionString))
{
builder.UseSqlServer(_nameOrConnectionString);
}
}
protected override void OnModelCreating(ModelBuilder builder)

View File

@ -13,18 +13,25 @@ using Microsoft.Data.Entity;
namespace Microsoft.AspNet.Identity.Entity
{
public class InMemoryInMemoryUserStore :
InMemoryUserStore<EntityUser>
public class InMemoryUserStore : InMemoryUserStore<EntityUser, IdentityContext>
{
public InMemoryInMemoryUserStore(DbContext context) : base(context) { }
public InMemoryUserStore(IdentityContext context) : base(context) { }
}
public class InMemoryUserStore<TUser> : InMemoryUserStore<TUser, EntityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> where TUser:EntityUser
public class InMemoryUserStore<TUser> : InMemoryUserStore<TUser, IdentityContext>
where TUser : EntityUser
{
public InMemoryUserStore(DbContext context) : base(context) { }
public InMemoryUserStore(IdentityContext context) : base(context) { }
}
public class InMemoryUserStore<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> :
public class InMemoryUserStore<TUser, TContext> : InMemoryUserStore<TUser, EntityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim, TContext>
where TUser:EntityUser
where TContext : DbContext
{
public InMemoryUserStore(TContext context) : base(context) { }
}
public class InMemoryUserStore<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim, TContext> :
IUserLoginStore<TUser>,
IUserClaimStore<TUser>,
IUserRoleStore<TUser>,
@ -41,10 +48,11 @@ namespace Microsoft.AspNet.Identity.Entity
where TUserLogin : IdentityUserLogin<TKey>, new()
where TUserRole : IdentityUserRole<TKey>, new()
where TUserClaim : IdentityUserClaim<TKey>, new()
where TContext : DbContext
{
private bool _disposed;
public InMemoryUserStore(DbContext context)
public InMemoryUserStore(TContext context)
{
if (context == null)
{
@ -54,7 +62,7 @@ namespace Microsoft.AspNet.Identity.Entity
AutoSaveChanges = true;
}
public DbContext Context { get; private set; }
public TContext Context { get; private set; }
/// <summary>
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync

View File

@ -23,9 +23,8 @@
<Compile Include="EntityRole.cs" />
<Compile Include="EntityRoleStore.cs" />
<Compile Include="EntityUser.cs" />
<Compile Include="IdentityBuilderExtensions.cs" />
<Compile Include="EntityIdentityBuilderExtensions.cs" />
<Compile Include="IdentityContext.cs" />
<Compile Include="IdentityEntityServiceCollectionExtensions.cs" />
<Compile Include="IdentitySqlContext.cs" />
<Compile Include="InMemoryUserStore.cs" />
<Compile Include="RoleStore.cs" />

View File

@ -10,9 +10,6 @@ namespace Microsoft.AspNet.Identity.Entity
public User()
{
Id = Guid.NewGuid().ToString();
// TODO: remove when bug is fixed
UserName = "";
PasswordHash = "";
}
public User(string userName) : this()

View File

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Identity.Security
{
public class HttpAuthenticationManager : IAuthenticationManager
{
public static readonly string TwoFactorUserIdAuthenticationType = "Microsoft.AspNet.Identity.TwoFactor.UserId";
public static readonly string TwoFactorRememberedAuthenticationType = "Microsoft.AspNet.Identity.TwoFactor.Remembered";
public HttpAuthenticationManager(IContextAccessor<HttpContext> contextAccessor)
{
Context = contextAccessor.Value;
}
public HttpContext Context { get; private set; }
public void ForgetClient()
{
Context.Response.SignOut(TwoFactorRememberedAuthenticationType);
}
public async Task<bool> IsClientRememeberedAsync(string userId)
{
var result =
await Context.AuthenticateAsync(TwoFactorRememberedAuthenticationType);
return (result != null && result.Identity != null && result.Identity.Name == userId);
}
public void RememberClient(string userId)
{
var rememberBrowserIdentity = new ClaimsIdentity(TwoFactorRememberedAuthenticationType);
rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
Context.Response.SignIn(rememberBrowserIdentity);
}
public async Task<string> RetrieveUserId()
{
var result = await Context.AuthenticateAsync(TwoFactorUserIdAuthenticationType);
if (result != null && result.Identity != null)
{
return result.Identity.Name;
}
return null;
}
public void SignIn(ClaimsIdentity identity, bool isPersistent)
{
Context.Response.SignIn(identity, new AuthenticationProperties { IsPersistent = isPersistent });
}
public void SignOut(string authenticationType)
{
Context.Response.SignOut(authenticationType);
}
public Task StoreUserId(string userId)
{
var userIdentity = new ClaimsIdentity(TwoFactorUserIdAuthenticationType);
userIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
Context.Response.SignIn(userIdentity);
return Task.FromResult(0);
}
}
}

View File

@ -8,18 +8,11 @@ namespace Microsoft.AspNet.Identity
{
public static class IdentityBuilderExtensions
{
// TODO: remove
public static IdentityBuilder<TUser, IdentityRole> AddSecurity<TUser>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : class
{
builder.Services.AddScoped<SignInManager<TUser>>();
return builder;
}
public static IdentityBuilder<TUser, IdentityRole> AddHttpSignIn<TUser>(this IdentityBuilder<TUser, IdentityRole> builder)
where TUser : class
{
builder.Services.AddScoped<SignInManager<TUser>>();
// todo: review should this be scoped?
builder.Services.AddTransient<IAuthenticationManager, HttpAuthenticationManager>();
return builder;
}
}

View File

@ -20,12 +20,9 @@
<Content Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="DefaultAuthenticationTypes.cs" />
<Compile Include="HttpAuthenticationManager.cs" />
<Compile Include="IdentityBuilderExtensions.cs" />
<Compile Include="IdentityExtensions.cs" />
<Compile Include="SecurityServiceCollectionExtensions.cs" />
<Compile Include="SignInManager.cs" />
<Compile Include="SignInStatus.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity.Security;
namespace Microsoft.Framework.DependencyInjection
{
public static class SecurityServiceCollectionExtensions
{
// todo: remove?
public static ServiceCollection AddSecurity<TUser>(this ServiceCollection services)
where TUser : class
{
services.AddTransient<SignInManager<TUser>>();
return services;
}
}
}

View File

@ -1,178 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Identity.Security
{
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
{
get { return _authType ?? DefaultAuthenticationTypes.ApplicationCookie; }
set { _authType = value; }
}
public TManager UserManager { get; private set; }
public HttpContext Context { get; private set; }
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user)
{
return await UserManager.CreateIdentityAsync(user, AuthenticationType);
}
public virtual async Task SignInAsync(TUser user, bool isPersistent, bool rememberBrowser)
{
// TODO: all the two factor logic/external/rememberBrowser
var userIdentity = await CreateUserIdentityAsync(user);
Context.Response.SignIn(userIdentity, new AuthenticationProperties { IsPersistent = isPersistent });
}
public virtual void SignOut()
{
Context.Response.SignOut(AuthenticationType);
}
//public virtual async Task<bool> SendTwoFactorCode(string provider)
//{
// var userId = await GetVerifiedUserId();
// if (userId == null)
// {
// return false;
// }
// var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider);
// // See IdentityConfig.cs to plug in Email/SMS services to actually send the code
// await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token);
// return true;
//}
//public Task<TKey> GetVerifiedUserId()
//{
// //var result = await AuthenticationManager.Authenticate(DefaultAuthenticationTypes.TwoFactorCookie);
// //if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserIdAsync()))
// //{
// // return result.Identity.GetUserIdAsync();
// //}
// return Task.FromResult(default(TKey));
//}
//public async Task<bool> HasBeenVerified()
//{
// return await GetVerifiedUserId() != null;
//}
//public virtual async Task<SignInStatus> TwoFactorSignIn(string provider, string code, bool isPersistent, bool rememberBrowser)
//{
// var userId = await GetVerifiedUserId();
// if (userId == null)
// {
// return SignInStatus.Failure;
// }
// var user = await UserManager.FindByIdAsync(userId);
// if (user == null)
// {
// return SignInStatus.Failure;
// }
// if (await UserManager.IsLockedOutAsync(user.Id))
// {
// return SignInStatus.LockedOut;
// }
// if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code))
// {
// // When token is verified correctly, clear the access failed count used for lockout
// await UserManager.ResetAccessFailedCountAsync(user.Id);
// await SignIn(user, isPersistent, rememberBrowser);
// return SignInStatus.Success;
// }
// // If the token is incorrect, record the failure which also may cause the user to be locked out
// await UserManager.AccessFailedAsync(user.Id);
// return SignInStatus.Failure;
//}
//public async Task<SignInStatus> ExternalSignIn(ExternalLoginInfo loginInfo, bool isPersistent)
//{
// var user = await UserManager.FindByLoginAsync(loginInfo.Login);
// if (user == null)
// {
// return SignInStatus.Failure;
// }
// if (await UserManager.IsLockedOutAsync(user.Id))
// {
// return SignInStatus.LockedOut;
// }
// return await SignInOrTwoFactor(user, isPersistent);
//}
//private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)
//{
// if (await UserManager.GetTwoFactorEnabledAsync(user.Id))
// //&& !await AuthenticationManager.TwoFactorBrowserRemembered(user.Id))
// {
// //var identity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie);
// //identity.AddClaimAsync(new Claim(ClaimTypes.NameIdentifier, user.Id));
// //AuthenticationManager.SignIn(identity);
// return SignInStatus.RequiresTwoFactorAuthentication;
// }
// await SignIn(user, isPersistent, false);
// return SignInStatus.Success;
//}
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
return SignInStatus.Failure;
}
if (UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
if (await UserManager.CheckPasswordAsync(user, password))
{
await SignInAsync(user, isPersistent, false);
return SignInStatus.Success;
//TODO: return await SignInOrTwoFactor(user, isPersistent);
}
if (UserManager.SupportsUserLockout && shouldLockout)
{
// If lockout is requested, increment access failed count which might lock out the user
await UserManager.AccessFailedAsync(user);
if (await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
}
return SignInStatus.Failure;
}
}
}

View File

@ -42,17 +42,5 @@ namespace Microsoft.AspNet.Identity
/// 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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Identity.Security
namespace Microsoft.AspNet.Identity
{
public static class DefaultAuthenticationTypes
{

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity
{
public interface IAuthenticationManager
{
void SignIn(ClaimsIdentity identity, bool isPersistent);
void SignOut(string authenticationType);
// remember browser for two factor
void ForgetClient();
void RememberClient(string userId);
Task<bool> IsClientRememeberedAsync(string userId);
// half cookie
Task StoreUserId(string userId);
Task<string> RetrieveUserId();
}
}

View File

@ -14,7 +14,6 @@ namespace Microsoft.AspNet.Identity
public IdentityOptions()
{
// TODO: Split into sub options
ClaimType = new ClaimTypeOptions();
User = new UserOptions();
Password = new PasswordOptions();
@ -28,18 +27,5 @@ namespace Microsoft.AspNet.Identity
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

@ -36,6 +36,7 @@ namespace Microsoft.Framework.DependencyInjection
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
services.AddScoped<UserManager<TUser>>();
services.AddScoped<SignInManager<TUser>>();
services.AddScoped<RoleManager<TRole>>();
return new IdentityBuilder<TUser, TRole>(services);
}

View File

@ -24,6 +24,8 @@
<Compile Include="ClaimsIdentityFactory.cs" />
<Compile Include="ClaimTypeOptions.cs" />
<Compile Include="Crypto.cs" />
<Compile Include="DefaultAuthenticationTypes.cs" />
<Compile Include="IAuthenticationManager.cs" />
<Compile Include="IClaimsIdentityFactory.cs" />
<Compile Include="IdentityBuilder.cs" />
<Compile Include="IdentityMessage.cs" />
@ -64,6 +66,8 @@
<Compile Include="Rfc6238AuthenticationService.cs" />
<Compile Include="RoleManager.cs" />
<Compile Include="RoleValidator.cs" />
<Compile Include="SignInManager.cs" />
<Compile Include="SignInStatus.cs" />
<Compile Include="UserLoginInfo.cs" />
<Compile Include="UserManager.cs" />
<Compile Include="UserOptions.cs" />

View File

@ -40,18 +40,5 @@ namespace Microsoft.AspNet.Identity
/// 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

@ -0,0 +1,176 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Interface that manages SignIn operations for a user
/// </summary>
/// <typeparam name="TUser"></typeparam>
public class SignInManager<TUser> where TUser : class
{
public SignInManager(UserManager<TUser> userManager, IAuthenticationManager authenticationManager)
{
if (userManager == null)
{
throw new ArgumentNullException("userManager");
}
if (authenticationManager == null)
{
throw new ArgumentNullException("authenticationManager");
}
UserManager = userManager;
AuthenticationManager = authenticationManager;
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;
}
// TODO: this should go into some kind of Options/setup
public string AuthenticationType { get; set; }
public UserManager<TUser> UserManager { get; private set; }
public IAuthenticationManager AuthenticationManager { get; private set; }
// Should this be a func?
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user)
{
return await UserManager.CreateIdentityAsync(user, AuthenticationType);
}
public virtual async Task SignInAsync(TUser user, bool isPersistent)
{
var userIdentity = await CreateUserIdentityAsync(user);
AuthenticationManager.SignIn(userIdentity, isPersistent);
}
// TODO: Should this be async?
public void SignOut()
{
AuthenticationManager.SignOut(AuthenticationType);
}
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
return SignInStatus.Failure;
}
if (UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
if (await UserManager.CheckPasswordAsync(user, password))
{
return await SignInOrTwoFactor(user, isPersistent);
}
if (UserManager.SupportsUserLockout && shouldLockout)
{
// If lockout is requested, increment access failed count which might lock out the user
await UserManager.AccessFailedAsync(user);
if (await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
}
return SignInStatus.Failure;
}
public virtual async Task<bool> SendTwoFactorCode(string provider)
{
var userId = await AuthenticationManager.RetrieveUserId();
if (userId == null)
{
return false;
}
var user = await UserManager.FindByIdAsync(userId);
if (user == null)
{
return false;
}
var token = await UserManager.GenerateTwoFactorTokenAsync(user, provider);
// See IdentityConfig.cs to plug in Email/SMS services to actually send the code
await UserManager.NotifyTwoFactorTokenAsync(user, provider, token);
return true;
}
//public async Task<bool> HasBeenVerified()
//{
// return await GetVerifiedUserId() != null;
//}
public virtual async Task RememberTwoFactorClient(TUser user)
{
var userId = await UserManager.GetUserIdAsync(user);
AuthenticationManager.RememberClient(userId);
}
public virtual Task ForgetTwoFactorClientAsync()
{
AuthenticationManager.ForgetClient();
return Task.FromResult(0);
}
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent)
{
var userId = await AuthenticationManager.RetrieveUserId();
if (userId == null)
{
return SignInStatus.Failure;
}
var user = await UserManager.FindByIdAsync(userId);
if (user == null)
{
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code))
{
// When token is verified correctly, clear the access failed count used for lockout
await UserManager.ResetAccessFailedCountAsync(user);
await SignInAsync(user, isPersistent);
return SignInStatus.Success;
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user);
return SignInStatus.Failure;
}
public async Task<SignInStatus> ExternalLoginSignInAsync(UserLoginInfo loginInfo, bool isPersistent)
{
var user = await UserManager.FindByLoginAsync(loginInfo);
if (user == null)
{
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user))
{
return SignInStatus.LockedOut;
}
return await SignInOrTwoFactor(user, isPersistent);
}
private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)
{
if (await UserManager.GetTwoFactorEnabledAsync(user))
{
var userId = await UserManager.GetUserIdAsync(user);
if (!await AuthenticationManager.IsClientRememeberedAsync(userId))
{
// Store the userId for use after two factor check
await AuthenticationManager.StoreUserId(userId);
return SignInStatus.RequiresVerification;
}
}
await SignInAsync(user, isPersistent);
return SignInStatus.Success;
}
}
}

View File

@ -1,14 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Identity.Security
namespace Microsoft.AspNet.Identity
{
public enum SignInStatus
{
Success,
LockedOut,
RequiresTwoFactorAuthentication,
RequiresVerification,
Failure
}
}

View File

@ -727,7 +727,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="newPassword"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> ResetPassword(TUser user, string token, string newPassword, CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task<IdentityResult> ResetPasswordAsync(TUser user, string token, string newPassword, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)

View File

@ -22,15 +22,5 @@ namespace Microsoft.AspNet.Identity
/// 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

@ -19,10 +19,6 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
public class InMemoryUserStoreTest
{
class ApplicationUserManager : UserManager<EntityUser>
{
public ApplicationUserManager(IServiceProvider services, IUserStore<EntityUser> store, IOptionsAccessor<IdentityOptions> options) : base(services, store, options) { }
}
[Fact]
public async Task CanUseAddedManagerInstance()
@ -30,11 +26,11 @@ namespace Microsoft.AspNet.Identity.Entity.Test
var services = new ServiceCollection();
services.AddEntityFramework().AddInMemoryStore();
services.AddSingleton<IOptionsAccessor<IdentityOptions>, OptionsAccessor<IdentityOptions>>();
services.AddInstance<DbContext>(new IdentityContext());
services.AddTransient<IUserStore<EntityUser>, InMemoryInMemoryUserStore>();
services.AddSingleton<ApplicationUserManager, ApplicationUserManager>();
services.AddInstance<IdentityContext>(new IdentityContext());
services.AddTransient<IUserStore<EntityUser>, InMemoryUserStore>();
services.AddSingleton<UserManager<EntityUser>>();
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationUserManager>();
var manager = provider.GetService<UserManager<EntityUser>>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello")));
}
@ -46,41 +42,23 @@ namespace Microsoft.AspNet.Identity.Entity.Test
services.AddEntityFramework().AddInMemoryStore();
// TODO: this needs to construct a new instance of InMemoryStore
var store = new InMemoryInMemoryUserStore(new IdentityContext());
var store = new InMemoryUserStore(new IdentityContext());
services.Add(OptionsServices.GetDefaultServices());
services.AddIdentity<EntityUser, EntityRole>(s =>
{
s.AddUserStore(() => store);
s.AddUserManager<ApplicationUserManager>();
});
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationUserManager>();
var manager = provider.GetService<UserManager<EntityUser>>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello2")));
}
//[Fact]
//public async Task CanUseSingletonGenericManagerInstance()
//{
// var services = new ServiceCollection();
// var store = new EntityUserStore(new IdentityContext());
// services.AddIdentity<EntityUser>(s =>
// {
// s.UseStore(() => store);
// s.UseManager<UserManager<EntityUser>>();
// });
// var provider = services.BuildServiceProvider();
// var manager = provider.GetService<UserManager<EntityUser>>();
// Assert.NotNull(manager);
// IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityUser("hello")));
//}
[Fact]
public async Task EntityUserStoreMethodsThrowWhenDisposedTest()
{
var store = new InMemoryInMemoryUserStore(new IdentityContext());
var store = new InMemoryUserStore(new IdentityContext());
store.Dispose();
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddClaimAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddLoginAsync(null, null));
@ -112,8 +90,8 @@ namespace Microsoft.AspNet.Identity.Entity.Test
[Fact]
public async Task EntityUserStorePublicNullCheckTest()
{
Assert.Throws<ArgumentNullException>("context", () => new InMemoryInMemoryUserStore(null));
var store = new InMemoryInMemoryUserStore(new IdentityContext());
Assert.Throws<ArgumentNullException>("context", () => new InMemoryUserStore(null));
var store = new InMemoryUserStore(new IdentityContext());
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserIdAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserNameAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetUserNameAsync(null, null));
@ -675,7 +653,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
Assert.NotNull(stamp);
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
IdentityResultAssert.IsSuccess(await manager.ResetPassword(user, token, newPassword));
IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword));
Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword));
Assert.NotEqual(stamp, user.SecurityStamp);
@ -695,7 +673,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
manager.PasswordValidator = new AlwaysBadValidator();
IdentityResultAssert.IsFailure(await manager.ResetPassword(user, token, newPassword),
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword),
AlwaysBadValidator.ErrorMessage);
Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password));
@ -713,7 +691,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsFailure(await manager.ResetPassword(user, "bogus", newPassword), "Invalid token.");
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token.");
Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(stamp, user.SecurityStamp);

View File

@ -28,4 +28,4 @@
<Compile Include="TestIdentityFactory.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -14,11 +14,6 @@ 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()
{
@ -29,11 +24,10 @@ namespace Microsoft.AspNet.Identity.Entity.Test
services.AddIdentity<EntityUser, EntityRole>(s =>
{
s.AddRoleStore(() => store);
s.AddRoleManager<ApplicationRoleManager>();
});
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationRoleManager>();
var manager = provider.GetService<RoleManager<EntityRole>>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityRole("arole")));
}
@ -42,13 +36,11 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
var services = new ServiceCollection();
services.AddEntityFramework().AddInMemoryStore();
services.AddTransient<DbContext, IdentityContext>();
services.AddTransient<IdentityContext>();
services.AddTransient<IRoleStore<EntityRole>, EntityRoleStore<EntityRole>>();
//todo: services.AddSingleton<RoleManager<EntityRole>, RoleManager<EntityRole>>();
// TODO: How to configure SqlServer?
services.AddSingleton<ApplicationRoleManager, ApplicationRoleManager>();
services.AddSingleton<RoleManager<EntityRole>>();
var provider = services.BuildServiceProvider();
var manager = provider.GetService<ApplicationRoleManager>();
var manager = provider.GetService<RoleManager<EntityRole>>();
Assert.NotNull(manager);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(new EntityRole("someRole")));
}
@ -56,7 +48,7 @@ namespace Microsoft.AspNet.Identity.Entity.Test
[Fact]
public async Task RoleStoreMethodsThrowWhenDisposedTest()
{
var store = new EntityRoleStore<EntityRole, string>(new IdentityContext());
var store = new EntityRoleStore<EntityRole>(new IdentityContext());
store.Dispose();
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByIdAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByNameAsync(null));
@ -71,8 +63,8 @@ namespace Microsoft.AspNet.Identity.Entity.Test
[Fact]
public async Task RoleStorePublicNullCheckTest()
{
Assert.Throws<ArgumentNullException>("context", () => new EntityRoleStore<EntityRole, string>(null));
var store = new EntityRoleStore<EntityRole, string>(new IdentityContext());
Assert.Throws<ArgumentNullException>("context", () => new EntityRoleStore<EntityRole>(null));
var store = new EntityRoleStore<EntityRole>(new IdentityContext());
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.GetRoleIdAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.GetRoleNameAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.SetRoleNameAsync(null, null));

View File

@ -17,11 +17,20 @@ namespace Microsoft.AspNet.Identity.Entity.Test
{
public class SqlUserStoreTest
{
private const string ConnectionString = @"Server=(localdb)\v11.0;Database=SqlUserStoreTest;Trusted_Connection=True;";
public class ApplicationUser : User { }
public class ApplicationDbContext : IdentitySqlContext<ApplicationUser>
{
public ApplicationDbContext(IServiceProvider services) : base(services) { }
public ApplicationDbContext(IServiceProvider services, IOptionsAccessor<DbContextOptions> options) : base(services, options.Options) { }
}
[TestPriority(0)]
[Fact]
public void RecreateDatabase()
{
CreateContext(true);
}
[Fact]
@ -32,9 +41,10 @@ namespace Microsoft.AspNet.Identity.Entity.Test
builder.UseServices(services =>
{
services.Add(OptionsServices.GetDefaultServices());
services.AddEntityFramework().AddSqlServer();
services.AddIdentityEntityFramework<ApplicationDbContext, ApplicationUser>();
services.AddIdentity<ApplicationUser>().AddEntityFramework<ApplicationUser, ApplicationDbContext>();
services.SetupOptions<DbContextOptions>(options =>
options.UseSqlServer(ConnectionString));
});
var userStore = builder.ApplicationServices.GetService<IUserStore<ApplicationUser>>();
@ -58,19 +68,23 @@ namespace Microsoft.AspNet.Identity.Entity.Test
var guid = Guid.NewGuid().ToString();
db.Users.Add(new User {Id = guid, UserName = guid});
db.SaveChanges();
Assert.True(db.Users.Any(u => u.UserName == guid));
Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid));
}
}
public static IdentitySqlContext CreateContext()
public static IdentitySqlContext CreateContext(bool delete = false)
{
var services = new ServiceCollection();
services.AddEntityFramework().AddSqlServer();
var serviceProvider = services.BuildServiceProvider();
var db = new IdentitySqlContext(serviceProvider);
var db = new IdentitySqlContext(serviceProvider, ConnectionString);
if (delete)
{
db.Database.EnsureDeleted();
}
db.Database.EnsureCreated();
return db;
}
@ -81,45 +95,20 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public static ApplicationDbContext CreateAppContext()
{
CreateContext();
var services = new ServiceCollection();
services.AddEntityFramework().AddSqlServer();
services.Add(OptionsServices.GetDefaultServices());
var serviceProvider = services.BuildServiceProvider();
var db = new ApplicationDbContext(serviceProvider);
// TODO: Recreate DB, doesn't support String ID or Identity context yet
var db = new ApplicationDbContext(serviceProvider, serviceProvider.GetService<IOptionsAccessor<DbContextOptions>>());
db.Database.EnsureCreated();
// TODO: CreateAsync DB?
return db;
}
public static UserManager<User> CreateManager(DbContext context)
{
var services = new ServiceCollection();
services.AddTransient<IUserValidator<User>, UserValidator<User>>();
services.AddTransient<IPasswordValidator<User>, PasswordValidator<User>>();
//services.AddInstance<IUserStore<User>>(new UserStore<User>(context));
//services.AddSingleton<UserManager<User>>();
var options = new IdentityOptions
{
Password = new PasswordOptions
{
RequireDigit = false,
RequireLowercase = false,
RequireNonLetterOrDigit = false,
RequireUppercase = false
},
User = new UserOptions
{
AllowOnlyAlphanumericNames = false
}
};
var optionsAccessor = new OptionsAccessor<IdentityOptions>(new[] { new TestIdentityFactory.TestSetup(options) });
//services.AddInstance<IOptionsAccessor<IdentityOptions>>(new OptionsAccessor<IdentityOptions>(new[] { new TestSetup(options) }));
//return services.BuildServiceProvider().GetService<UserManager<User>>();
return new UserManager<User>(services.BuildServiceProvider(), new UserStore<User>(context), optionsAccessor);
return MockHelpers.CreateManager(() => new UserStore<User>(context));
}
public static UserManager<User> CreateManager()
@ -151,13 +140,14 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task CanUpdateUserName()
{
var manager = CreateManager();
var user = new User("UpdateAsync");
var oldName = Guid.NewGuid().ToString();
var user = new User(oldName);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
Assert.Null(await manager.FindByNameAsync("New"));
user.UserName = "New";
var newName = Guid.NewGuid().ToString();
user.UserName = newName;
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
Assert.NotNull(await manager.FindByNameAsync("New"));
Assert.Null(await manager.FindByNameAsync("UpdateAsync"));
Assert.NotNull(await manager.FindByNameAsync(newName));
Assert.Null(await manager.FindByNameAsync(oldName));
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
}
@ -165,12 +155,13 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public async Task CanSetUserName()
{
var manager = CreateManager();
var user = new User("UpdateAsync");
var oldName = Guid.NewGuid().ToString();
var user = new User(oldName);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
Assert.Null(await manager.FindByNameAsync("New"));
IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, "New"));
Assert.NotNull(await manager.FindByNameAsync("New"));
Assert.Null(await manager.FindByNameAsync("UpdateAsync"));
var newName = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName));
Assert.NotNull(await manager.FindByNameAsync(newName));
Assert.Null(await manager.FindByNameAsync(oldName));
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
}

View File

@ -3,7 +3,6 @@
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Identity.Test;
using Microsoft.Data.Entity;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
@ -16,95 +15,39 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public class StartupTest
{
public class ApplicationUser : EntityUser { }
public class ApplicationUserManager : UserManager<ApplicationUser>
public class ApplicationDbContext : IdentityContext<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.Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services => {
services.Add(OptionsServices.GetDefaultServices());
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.Builder(new ServiceCollection().BuildServiceProvider());
app.UseServices(services => {
services.Add(OptionsServices.GetDefaultServices());
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);
public ApplicationDbContext(IServiceProvider services) : base(services) { }
}
[Fact]
public async Task EnsureStartupUsageWorks()
{
EnsureDatabase();
IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services =>
{
services.Add(OptionsServices.GetDefaultServices());
services.AddEntityFramework();
services.AddTransient<DbContext, IdentityContext>();
services.AddEntityFramework().AddInMemoryStore();
services.AddIdentity<ApplicationUser, EntityRole>(s =>
{
s.AddEntity();
s.AddUserManager<ApplicationUserManager>();
s.AddRoleManager<ApplicationRoleManager>();
s.AddEntityFrameworkInMemory<ApplicationUser, EntityRole, ApplicationDbContext>();
});
});
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>();
var userManager = builder.ApplicationServices.GetService<UserManager<ApplicationUser>>();
var roleManager = builder.ApplicationServices.GetService<RoleManager<EntityRole>>();
Assert.NotNull(userStore);
Assert.NotNull(userManager);
Assert.NotNull(roleStore);
Assert.NotNull(roleManager);
//await CreateAdminUser(builder.ApplicationServices);
await CreateAdminUser(builder.ApplicationServices);
}
private static async Task CreateAdminUser(IServiceProvider serviceProvider)
@ -112,13 +55,23 @@ namespace Microsoft.AspNet.Identity.Entity.Test
const string userName = "admin";
const string roleName = "Admins";
const string password = "1qaz@WSX";
var userManager = serviceProvider.GetService<ApplicationUserManager>();
var roleManager = serviceProvider.GetService<ApplicationRoleManager>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
var roleManager = serviceProvider.GetService<RoleManager<EntityRole>>();
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));
}
public static void EnsureDatabase()
{
var services = new ServiceCollection();
services.AddEntityFramework().AddInMemoryStore();
var serviceProvider = services.BuildServiceProvider();
var db = new ApplicationDbContext(serviceProvider);
db.Database.EnsureCreated();
}
}
}

View File

@ -1,10 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity.Test;
using Microsoft.Data.Entity;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using System;
namespace Microsoft.AspNet.Identity.Entity.Test
{
@ -13,61 +15,18 @@ namespace Microsoft.AspNet.Identity.Entity.Test
public static IdentityContext CreateContext()
{
var services = new ServiceCollection();
//#if NET45
// services.AddEntityFramework().AddSqlServer();
//#else
services.AddEntityFramework().AddInMemoryStore();
//#endif
var serviceProvider = services.BuildServiceProvider();
var db = new IdentityContext(serviceProvider);
// TODO: Recreate DB, doesn't support String ID or Identity context yet
db.Database.EnsureCreated();
// TODO: CreateAsync DB?
return db;
}
public class TestSetup : IOptionsSetup<IdentityOptions>
public static UserManager<EntityUser> CreateManager(IdentityContext context)
{
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 services = new ServiceCollection();
services.AddTransient<IUserValidator<EntityUser>, UserValidator<EntityUser>>();
services.AddTransient<IPasswordValidator<IdentityUser>, PasswordValidator<IdentityUser>>();
services.AddInstance<IUserStore<EntityUser>>(new InMemoryUserStore<EntityUser>(context));
services.AddSingleton<UserManager<EntityUser>, UserManager<EntityUser>>();
var options = new IdentityOptions
{
Password = new PasswordOptions
{
RequireDigit = false,
RequireLowercase = false,
RequireNonLetterOrDigit = false,
RequireUppercase = false
}
};
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 InMemoryUserStore<EntityUser>(context), optionsAccessor);
return MockHelpers.CreateManager<EntityUser>(() => new InMemoryUserStore<EntityUser>(context));
}
public static UserManager<EntityUser> CreateManager()
@ -75,13 +34,12 @@ namespace Microsoft.AspNet.Identity.Entity.Test
return CreateManager(CreateContext());
}
public static RoleManager<EntityRole> CreateRoleManager(DbContext context)
public static RoleManager<EntityRole> CreateRoleManager(IdentityContext context)
{
var services = new ServiceCollection();
services.AddTransient<IRoleValidator<EntityRole>, RoleValidator<EntityRole>>();
services.AddInstance<IRoleStore<EntityRole>>(new EntityRoleStore<EntityRole, string>(context));
// return services.BuildServiceProvider().GetService<RoleManager<EntityRole>>();
return new RoleManager<EntityRole>(services.BuildServiceProvider(), new EntityRoleStore<EntityRole, string>(context));
services.Add(OptionsServices.GetDefaultServices());
services.AddIdentity<EntityUser, EntityRole>(b => b.AddRoleStore(() => new EntityRoleStore<EntityRole>(context)));
return services.BuildServiceProvider().GetService<RoleManager<EntityRole>>();
}
public static RoleManager<EntityRole> CreateRoleManager()

View File

@ -16,24 +16,17 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
services.Add(OptionsServices.GetDefaultServices());
services.AddTransient<IUserValidator<IdentityUser>, UserValidator<IdentityUser>>();
services.AddTransient<IPasswordValidator<IdentityUser>, PasswordValidator<IdentityUser>>();
var options = new IdentityOptions
services.AddSingleton<IUserStore<IdentityUser>, InMemoryUserStore<IdentityUser>>();
services.AddSingleton<UserManager<IdentityUser>>();
services.SetupOptions<IdentityOptions>(options =>
{
Password = new PasswordOptions {
RequireDigit = false,
RequireLowercase = false,
RequireNonLetterOrDigit = false,
RequireUppercase = false
},
User = new UserOptions {
AllowOnlyAlphanumericNames = false
}
};
var optionsAccessor = new OptionsAccessor<IdentityOptions>(new[] {new TestSetup(options)});
//services.AddInstance<IOptionsAccessor<IdentityOptions>>(optionsAccessor);
//services.AddInstance<IUserStore<IdentityUser>>(new InMemoryUserStore<IdentityUser>());
//services.AddSingleton<UserManager<IdentityUser>, UserManager<IdentityUser>>();
//return services.BuildServiceProvider().GetService<UserManager<IdentityUser>>();
return new UserManager<IdentityUser>(services.BuildServiceProvider(), new InMemoryUserStore<IdentityUser>(), optionsAccessor);
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonLetterOrDigit = false;
options.Password.RequireUppercase = false;
options.User.AllowOnlyAlphanumericNames = false;
});
return services.BuildServiceProvider().GetService<UserManager<IdentityUser>>();
}
protected override RoleManager<IdentityRole> CreateRoleManager()
@ -41,8 +34,8 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
var services = new ServiceCollection();
services.AddTransient<IRoleValidator<IdentityRole>, RoleValidator<IdentityRole>>();
services.AddInstance<IRoleStore<IdentityRole>>(new InMemoryRoleStore<IdentityRole>());
//return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
return new RoleManager<IdentityRole>(services.BuildServiceProvider(), new InMemoryRoleStore<IdentityRole>());
services.AddSingleton<RoleManager<IdentityRole>>();
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
}
}
}

View File

@ -25,4 +25,4 @@
<Compile Include="StartupTest.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -1,13 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Identity.Test;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using System;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Identity.InMemory.Test
@ -16,56 +16,6 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
{
public class ApplicationUser : IdentityUser { }
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
{
public int Order { get { return 0; } }
public void Setup(IdentityOptions options)
{
options.Password.RequiredLength = -1;
}
}
[Fact]
public void CanCustomizeIdentityOptions()
{
var builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services => {
services.Add(OptionsServices.GetDefaultServices());
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()
{
var app = new Builder.Builder(new ServiceCollection().BuildServiceProvider());
app.UseServices(services =>
{
services.Add(OptionsServices.GetDefaultServices());
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()
{
@ -73,7 +23,6 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
builder.UseServices(services => services.AddIdentity<ApplicationUser>(s =>
{
services.Add(OptionsServices.GetDefaultServices());
s.AddInMemory();
}));
@ -96,7 +45,6 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
var builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services =>
{
services.Add(OptionsServices.GetDefaultServices());
services.AddIdentity<ApplicationUser>(s => s.AddInMemory());
});
@ -116,6 +64,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
var userManager2 = builder.ApplicationServices.GetService<UserManager<ApplicationUser>>();
var roleManager2 = builder.ApplicationServices.GetService<RoleManager<IdentityRole>>();
// Stores are singleton, managers are scoped
Assert.Equal(userStore, userStore2);
Assert.Equal(userManager, userManager2);
Assert.Equal(roleStore, roleStore2);

View File

@ -9,7 +9,6 @@ using Microsoft.AspNet.Identity.Test;
using Microsoft.AspNet.Security.Cookies;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
using Moq;
using System.Security.Claims;
@ -19,7 +18,9 @@ using Microsoft.AspNet.Builder;
namespace Microsoft.AspNet.Identity.Security.Test
{
public class SignInManagerTest
public class ApplicationUser : IdentityUser { }
public class HttpSignInTest
{
#if NET45
[Theory]
@ -43,7 +44,6 @@ namespace Microsoft.AspNet.Identity.Security.Test
{
services.Add(OptionsServices.GetDefaultServices());
services.AddInstance(contextAccessor.Object);
services.AddInstance<ILoggerFactory>(new NullLoggerFactory());
services.AddIdentity<ApplicationUser, IdentityRole>(s =>
{
s.AddInMemory();
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
// s.AddRoleStore(() => new InMemoryRoleStore<IdentityRole>());
// s.AddRoleManager<ApplicationRoleManager>();
// });
// services.AddTransient<ApplicationSignInManager>();
// services.AddTransient<ApplicationHttpSignInManager>();
// });
// // Act
@ -105,10 +105,10 @@ namespace Microsoft.AspNet.Identity.Security.Test
// };
// const string password = "Yol0Sw@g!";
// var userManager = app.ApplicationServices.GetService<ApplicationUserManager>();
// var signInManager = app.ApplicationServices.GetService<ApplicationSignInManager>();
// var HttpSignInManager = app.ApplicationServices.GetService<ApplicationHttpSignInManager>();
// IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
// var result = await signInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
// var result = await HttpSignInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
// // Assert
// Assert.Equal(SignInStatus.Success, result);
@ -120,41 +120,38 @@ namespace Microsoft.AspNet.Identity.Security.Test
{
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null));
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, null));
Assert.Throws<ArgumentNullException>("authenticationManager", () => new SignInManager<IdentityUser>(userManager, null));
}
//TODO: Mock fails in K (this works fine in net45)
[Fact]
public async Task EnsureClaimsIdentityFactoryCreateIdentityCalled()
{
// Setup
var user = new TestUser { UserName = "Foo" };
var userManager = MockHelpers.TestUserManager<TestUser>();
var identityFactory = new Mock<IClaimsIdentityFactory<TestUser>>();
const string authType = "Test";
var testIdentity = new ClaimsIdentity(authType);
identityFactory.Setup(s => s.CreateAsync(userManager, user, authType, CancellationToken.None)).ReturnsAsync(testIdentity).Verifiable();
userManager.ClaimsIdentityFactory = identityFactory.Object;
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(testIdentity, It.IsAny<AuthenticationProperties>())).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(userManager, contextAccessor.Object)
{
AuthenticationType = authType
};
//[Fact]
//public async Task EnsureClaimsIdentityFactoryCreateIdentityCalled()
//{
// // Setup
// var user = new TestUser { UserName = "Foo" };
// var userManager = MockHelpers.TestUserManager<TestUser>();
// var identityFactory = new Mock<IClaimsIdentityFactory<TestUser>>();
// const string authType = "Test";
// var testIdentity = new ClaimsIdentity(authType);
// identityFactory.Setup(s => s.CreateAsync(userManager, user, authType, CancellationToken.None)).ReturnsAsync(testIdentity).Verifiable();
// userManager.ClaimsIdentityFactory = identityFactory.Object;
// var context = new Mock<HttpContext>();
// var response = new Mock<HttpResponse>();
// context.Setup(c => c.Response).Returns(response.Object).Verifiable();
// response.Setup(r => r.SignIn(testIdentity, It.IsAny<AuthenticationProperties>())).Verifiable();
// var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
// contextAccessor.Setup(a => a.Value).Returns(context.Object);
// var helper = new HttpAuthenticationManager(contextAccessor.Object);
// Act
await helper.SignInAsync(user, false, false);
// // Act
// helper.SignIn(user, false);
// Assert
identityFactory.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
response.VerifyAll();
}
// // Assert
// identityFactory.VerifyAll();
// context.VerifyAll();
// contextAccessor.VerifyAll();
// response.VerifyAll();
//}
[Fact]
public async Task PasswordSignInReturnsLockedOutWhenLockedOut()
@ -168,7 +165,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
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);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
@ -197,7 +194,138 @@ namespace Microsoft.AspNet.Identity.Security.Test
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);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Fact]
public async Task PasswordSignInRequiresVerification()
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
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.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
response.Setup(r => r.SignIn(It.Is<ClaimsIdentity>(id => id.Name == user.Id))).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
// Assert
Assert.Equal(SignInStatus.RequiresVerification, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task CanTwoFactorSignIn(bool isPersistent)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
var provider = "twofactorprovider";
var code = "123456";
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.FindByIdAsync(user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code, CancellationToken.None)).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
response.Setup(r => r.SignIn(It.IsAny<ClaimsIdentity>(), It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType);
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType)).ReturnsAsync(authResult).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent);
// Assert
Assert.Equal(SignInStatus.Success, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Fact]
public void RememberClientStoresUserId()
{
// Setup
var user = new TestUser { UserName = "Foo" };
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.Is<ClaimsIdentity>(i => i.AuthenticationType == HttpAuthenticationManager.TwoFactorRememberedAuthenticationType))).Verifiable();
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType);
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
// Act
signInService.RememberClient(user.Id);
// Assert
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task RememberBrowserSkipsTwoFactorVerificationSignIn(bool isPersistent)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
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.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<ClaimsIdentity>(i => i.AuthenticationType == DefaultAuthenticationTypes.ApplicationCookie), It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType);
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType)).ReturnsAsync(authResult).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
var helper = new SignInManager<TestUser>(manager.Object, signInService);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
@ -223,7 +351,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
response.Setup(r => r.SignOut(authenticationType)).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object)
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object))
{
AuthenticationType = authenticationType
};
@ -249,7 +377,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
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);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
@ -258,7 +386,6 @@ namespace Microsoft.AspNet.Identity.Security.Test
manager.VerifyAll();
}
[Fact]
public async Task PasswordSignInFailsWithUnknownUser()
{
@ -268,7 +395,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
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);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
@ -297,7 +424,7 @@ namespace Microsoft.AspNet.Identity.Security.Test
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);
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object));
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
@ -307,30 +434,5 @@ 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

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

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
@ -37,13 +38,6 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(ClaimTypeOptions.DefaultSecurityStampClaimType, options.ClaimType.SecurityStamp);
}
[Fact]
public void CopyNullIsNoop()
{
var options = new IdentityOptions();
options.Copy(null);
}
[Fact]
public void IdentityOptionsFromConfig()
{
@ -91,47 +85,54 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(1000, options.Lockout.MaxFailedAccessAttempts);
}
//[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);
//}
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
{
public int Order { get { return 0; } }
public void Setup(IdentityOptions options)
{
options.Password.RequiredLength = -1;
}
}
[Fact]
public void CanCustomizeIdentityOptions()
{
var builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider());
builder.UseServices(services =>
{
services.AddIdentity<IdentityUser>();
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()
{
var app = new Builder.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 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

@ -34,4 +34,4 @@
<Compile Include="UserValidatorTest.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -503,7 +503,7 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.GeneratePasswordResetTokenAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.ResetPassword(null, null, null));
async () => await manager.ResetPasswordAsync(null, null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.IsEmailConfirmedAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
@ -594,7 +594,7 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.UpdateSecurityStampAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GetSecurityStampAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GeneratePasswordResetTokenAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ResetPassword(null, null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ResetPasswordAsync(null, null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GenerateEmailConfirmationTokenAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.IsEmailConfirmedAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ConfirmEmailAsync(null, null));

View File

@ -3,8 +3,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Xunit;
namespace Microsoft.AspNet.Identity.Test

View File

@ -1,7 +1,10 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Http" : "0.1-alpha-*",
"Microsoft.AspNet.Identity" : "0.1-alpha-*",
"Microsoft.AspNet.PipelineCore" : "0.1-alpha-*",
"Microsoft.AspNet.RequestContainer" : "0.1-alpha-*",
"Microsoft.AspNet.Testing" : "0.1-alpha-*",
"Microsoft.Framework.ConfigurationModel" : "0.1-alpha-*",
"Microsoft.Framework.DependencyInjection" : "0.1-alpha-*",

View File

@ -7,11 +7,28 @@ using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using Moq;
using System;
namespace Microsoft.AspNet.Identity.Test
{
public static class MockHelpers
{
public static UserManager<TUser> CreateManager<TUser>(Func<IUserStore<TUser>> storeFunc) where TUser : class
{
var services = new ServiceCollection();
services.Add(OptionsServices.GetDefaultServices());
services.AddIdentity<TUser>(b => b.AddUserStore(storeFunc));
services.SetupOptions<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonLetterOrDigit = false;
options.Password.RequireUppercase = false;
options.User.AllowOnlyAlphanumericNames = false;
});
return services.BuildServiceProvider().GetService<UserManager<TUser>>();
}
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
{
var store = new Mock<IUserStore<TUser>>();

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Identity.Test
{
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit.Abstractions;
using Xunit.Sdk;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestPriorityAttribute : Attribute
{
public TestPriorityAttribute(int priority)
{
Priority = priority;
}
public int Priority { get; private set; }
}
public class PriorityOrderer : ITestCaseOrderer
{
public IEnumerable<XunitTestCase> OrderTestCases<XunitTestCase>(IEnumerable<XunitTestCase> testCases) where XunitTestCase : ITestCase
{
var sortedMethods = new SortedDictionary<int, List<XunitTestCase>>();
foreach (XunitTestCase testCase in testCases)
{
int priority = 0;
foreach (IAttributeInfo attr in testCase.Method.GetCustomAttributes((typeof(TestPriorityAttribute))))
priority = attr.GetNamedArgument<int>("Priority");
GetOrCreate(sortedMethods, priority).Add(testCase);
}
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
{
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.Method.Name, y.Method.Name));
foreach (XunitTestCase testCase in list)
yield return testCase;
}
}
static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key) where TValue : new()
{
TValue result;
if (dictionary.TryGetValue(key, out result)) return result;
result = new TValue();
dictionary[key] = result;
return result;
}
}
}

View File

@ -466,7 +466,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(stamp);
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
IdentityResultAssert.IsSuccess(await manager.ResetPassword(user, token, newPassword));
IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword));
Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword));
Assert.NotEqual(stamp, user.SecurityStamp);
@ -486,7 +486,7 @@ namespace Microsoft.AspNet.Identity.Test
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
manager.PasswordValidator = new AlwaysBadValidator();
IdentityResultAssert.IsFailure(await manager.ResetPassword(user, token, newPassword),
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword),
AlwaysBadValidator.ErrorMessage);
Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password));
@ -504,7 +504,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsFailure(await manager.ResetPassword(user, "bogus", newPassword), "Invalid token.");
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token.");
Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password));
Assert.Equal(stamp, user.SecurityStamp);
@ -1443,21 +1443,5 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus"));
}
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);
}
}
}
}