diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs b/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs deleted file mode 100644 index 7f5752e4d3..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs +++ /dev/null @@ -1,22 +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.EntityFramework; -using Microsoft.Data.Entity; - -namespace Microsoft.Framework.DependencyInjection -{ - public static class EntityIdentityBuilderExtensions - { - // todo: add overloads - public static IdentityBuilder AddEntityFramework(this IdentityBuilder builder) - where TUser : User where TContext : DbContext - { - builder.Services.AddScoped, UserStore>(); - builder.Services.AddScoped, RoleStore>(); - builder.Services.AddScoped(); - return builder; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs index d1627e7b57..456651af4c 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs @@ -8,7 +8,7 @@ using Microsoft.Data.Entity.Metadata; namespace Microsoft.AspNet.Identity.EntityFramework { public class IdentityDbContext : - IdentityDbContext + IdentityDbContext { public IdentityDbContext() { } public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { } @@ -18,8 +18,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework } public class IdentityDbContext : - IdentityDbContext - where TUser : User + IdentityDbContext + where TUser : IdentityUser { public IdentityDbContext() { } public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { } @@ -28,16 +28,17 @@ namespace Microsoft.AspNet.Identity.EntityFramework public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } } - public class IdentityDbContext : DbContext - where TUser : User - where TRole : IdentityRole + public class IdentityDbContext : DbContext + where TUser : IdentityUser + where TRole : IdentityRole + where TKey : IEquatable { public DbSet Users { get; set; } - public DbSet UserClaims { get; set; } - public DbSet UserLogins { get; set; } - public DbSet UserRoles { get; set; } + public DbSet> UserClaims { get; set; } + public DbSet> UserLogins { get; set; } + public DbSet> UserRoles { get; set; } public DbSet Roles { get; set; } - public DbSet RoleClaims { get; set; } + public DbSet> RoleClaims { get; set; } private readonly string _nameOrConnectionString; @@ -70,15 +71,19 @@ namespace Microsoft.AspNet.Identity.EntityFramework .Properties(ps => ps.Property(r => r.Name)) .ToTable("AspNetRoles"); - builder.Entity() + builder.Entity>() .Key(uc => uc.Id) .ToTable("AspNetUserClaims"); + builder.Entity>() + .Key(uc => uc.Id) + .ToTable("AspNetRoleClaims"); + var userType = builder.Model.GetEntityType(typeof(TUser)); var roleType = builder.Model.GetEntityType(typeof(TRole)); - var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim)); - var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim)); - var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole)); + var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim)); + var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim)); + var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole)); var ucfk = userClaimType.AddForeignKey(userType.GetKey(), new[] { userClaimType.GetProperty("UserId") }); userType.AddNavigation(new Navigation(ucfk, "Claims", false)); //userClaimType.AddNavigation(new Navigation(ucfk, "User", true)); @@ -91,15 +96,16 @@ namespace Microsoft.AspNet.Identity.EntityFramework var rcfk = roleClaimType.AddForeignKey(roleType.GetKey(), new[] { roleClaimType.GetProperty("RoleId") }); roleType.AddNavigation(new Navigation(rcfk, "Claims", false)); - builder.Entity() + builder.Entity>() .Key(r => new { r.UserId, r.RoleId }) + // Blocks delete currently without cascade //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) //.ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)); .ToTable("AspNetUserRoles"); - builder.Entity() + builder.Entity>() .Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId }) - //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)); + //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) .ToTable("AspNetUserLogins"); } } diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityEntityFrameworkServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityEntityFrameworkServiceCollectionExtensions.cs new file mode 100644 index 0000000000..86f0f70a18 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityEntityFrameworkServiceCollectionExtensions.cs @@ -0,0 +1,56 @@ +// 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.EntityFramework; +using Microsoft.Data.Entity; +using System; + +namespace Microsoft.Framework.DependencyInjection +{ + public static class IdentityEntityFrameworkServiceCollectionExtensions + { + public static IdentityBuilder AddIdentitySqlServer(this ServiceCollection services) + { + return services.AddIdentitySqlServer(); + } + + public static IdentityBuilder AddIdentitySqlServer(this ServiceCollection services) + where TContext : DbContext + { + return services.AddIdentitySqlServer(); + } + + public static IdentityBuilder AddIdentitySqlServer(this ServiceCollection services) + where TUser : IdentityUser, new() + where TContext : DbContext + { + return services.AddIdentitySqlServer(); + } + + public static IdentityBuilder AddIdentitySqlServer(this ServiceCollection services) + where TUser : IdentityUser, new() + where TRole : IdentityRole, new() + where TContext : DbContext + { + var builder = services.AddIdentity(); + services.AddScoped, UserStore>(); + services.AddScoped, RoleStore>(); + services.AddScoped(); + return builder; + } + + public static IdentityBuilder AddIdentitySqlServer(this ServiceCollection services) + where TUser : IdentityUser, new() + where TRole : IdentityRole, new() + where TContext : DbContext + where TKey : IEquatable + { + var builder = services.AddIdentity(); + services.AddScoped, UserStore>(); + services.AddScoped, RoleStore>(); + services.AddScoped(); + return builder; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj b/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj index d9cdbd94b5..d492488c13 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj +++ b/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj @@ -21,11 +21,10 @@ - + - diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs index cf24e4e785..9964924857 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs @@ -12,22 +12,22 @@ using Microsoft.Data.Entity; namespace Microsoft.AspNet.Identity.EntityFramework { - public class RoleStore : RoleStore where TRole : IdentityRole + public class RoleStore : RoleStore where TRole : IdentityRole { public RoleStore(DbContext context) : base(context) { } } - public class RoleStore : RoleStore + public class RoleStore : RoleStore where TRole : IdentityRole where TContext : DbContext { public RoleStore(TContext context) : base(context) { } } - public class RoleStore : + public class RoleStore : IQueryableRoleStore, IRoleClaimStore - where TRole : IdentityRole + where TRole : IdentityRole where TKey : IEquatable where TContext : DbContext { @@ -107,7 +107,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("role"); } - return Task.FromResult(role.Id); + return Task.FromResult(ConvertIdToString(role.Id)); } public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = new CancellationToken()) @@ -133,9 +133,22 @@ namespace Microsoft.AspNet.Identity.EntityFramework return Task.FromResult(0); } - public virtual TKey ConvertId(string userId) + public virtual TKey ConvertIdFromString(string id) { - return (TKey)Convert.ChangeType(userId, typeof(TKey)); + if (id == null) + { + return default(TKey); + } + return (TKey)Convert.ChangeType(id, typeof(TKey)); + } + + public virtual string ConvertIdToString(TKey id) + { + if (id.Equals(default(TKey))) + { + return null; + } + return id.ToString(); } /// @@ -148,7 +161,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - var roleId = ConvertId(id); + var roleId = ConvertIdFromString(id); return GetRoleAggregate(u => u.Id.Equals(roleId), cancellationToken); } @@ -188,7 +201,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("role"); } - var result = RoleClaims.Where(rc => rc.RoleId == role.Id).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + var result = RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); return Task.FromResult((IList)result); } @@ -203,7 +216,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("claim"); } - RoleClaims.Add(new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); + RoleClaims.Add(new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); return Task.FromResult(0); } @@ -231,6 +244,6 @@ namespace Microsoft.AspNet.Identity.EntityFramework get { return Context.Set(); } } - private DbSet RoleClaims { get { return Context.Set(); } } + private DbSet> RoleClaims { get { return Context.Set>(); } } } } diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/User.cs b/src/Microsoft.AspNet.Identity.EntityFramework/User.cs deleted file mode 100644 index a32252b738..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/User.cs +++ /dev/null @@ -1,92 +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.Collections.Generic; - -namespace Microsoft.AspNet.Identity.EntityFramework -{ - public class User - { - public User() - { - Id = Guid.NewGuid().ToString(); - Claims = new List(); - Roles = new List(); - Logins = new List(); - } - - public User(string userName) : this() - { - UserName = userName; - } - - public virtual string Id { get; set; } - public virtual string UserName { get; set; } - - /// - /// Email - /// - public virtual string Email { get; set; } - - /// - /// True if the email is confirmed, default is false - /// - public virtual bool EmailConfirmed { get; set; } - - /// - /// The salted/hashed form of the user password - /// - public virtual string PasswordHash { get; set; } - - /// - /// A random value that should change whenever a users credentials have changed (password changed, login removed) - /// - public virtual string SecurityStamp { get; set; } - - /// - /// PhoneNumber for the user - /// - public virtual string PhoneNumber { get; set; } - - /// - /// True if the phone number is confirmed, default is false - /// - public virtual bool PhoneNumberConfirmed { get; set; } - - /// - /// Is two factor enabled for the user - /// - public virtual bool TwoFactorEnabled { get; set; } - - /// - /// DateTime in UTC when lockout ends, any time in the past is considered not locked out. - /// - public virtual DateTime? LockoutEnd { get; set; } - - /// - /// Is lockout enabled for this user - /// - public virtual bool LockoutEnabled { get; set; } - - /// - /// Used to record failures for the purposes of lockout - /// - public virtual int AccessFailedCount { get; set; } - - /// - /// Roles for the user - /// - public virtual ICollection Roles { get; private set; } - - /// - /// Claims for the user - /// - public virtual ICollection Claims { get; private set; } - - /// - /// Associated logins for the user - /// - public virtual ICollection Logins { get; private set; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs index 3f62c9cd72..2b91ac76d6 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs @@ -13,17 +13,25 @@ using Microsoft.Data.Entity; namespace Microsoft.AspNet.Identity.EntityFramework { - public class UserStore : UserStore + public class UserStore : UserStore { public UserStore(DbContext context) : base(context) { } } - public class UserStore : UserStore where TUser : User + public class UserStore : UserStore where TUser : IdentityUser, new() { public UserStore(DbContext context) : base(context) { } } - public class UserStore : + public class UserStore : UserStore + where TUser : IdentityUser, new() + where TRole : IdentityRole, new() + where TContext : DbContext + { + public UserStore(TContext context) : base(context) { } + } + + public class UserStore : IUserLoginStore, IUserRoleStore, IUserClaimStore, @@ -34,9 +42,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework IUserPhoneNumberStore, IQueryableUserStore, IUserTwoFactorStore - where TUser : User - where TRole : IdentityRole + where TUser : IdentityUser + where TRole : IdentityRole where TContext : DbContext + where TKey : IEquatable { private bool _disposed; @@ -79,7 +88,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("user"); } - return Task.FromResult(Convert.ToString(user.Id, CultureInfo.InvariantCulture)); + return Task.FromResult(ConvertIdToString(user.Id)); } public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) @@ -151,7 +160,26 @@ namespace Microsoft.AspNet.Identity.EntityFramework { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - return GetUserAggregate(u => u.Id.Equals(userId), cancellationToken); + var id = ConvertIdFromString(userId); + return GetUserAggregate(u => u.Id.Equals(id), cancellationToken); + } + + public virtual TKey ConvertIdFromString(string id) + { + if (id == null) + { + return default(TKey); + } + return (TKey)Convert.ChangeType(id, typeof(TKey)); + } + + public virtual string ConvertIdToString(TKey id) + { + if (id.Equals(default(TKey))) + { + return null; + } + return id.ToString(); } /// @@ -244,7 +272,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, roleName)); } - var ur = new IdentityUserRole { UserId = user.Id, RoleId = roleEntity.Id }; + var ur = new IdentityUserRole { UserId = user.Id, RoleId = roleEntity.Id }; // TODO: rely on fixup? UserRoles.Add(ur); user.Roles.Add(ur); @@ -274,7 +302,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework var roleEntity = Roles.SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); if (roleEntity != null) { - var userRole = UserRoles.FirstOrDefault(r => roleEntity.Id.Equals(r.RoleId) && r.UserId == user.Id); + var userRole = UserRoles.FirstOrDefault(r => roleEntity.Id.Equals(r.RoleId) && r.UserId.Equals(user.Id)); if (userRole != null) { UserRoles.Remove(userRole); @@ -356,9 +384,9 @@ namespace Microsoft.AspNet.Identity.EntityFramework } private DbSet Roles { get { return Context.Set(); } } - private DbSet UserClaims { get { return Context.Set(); } } - private DbSet UserRoles { get { return Context.Set(); } } - private DbSet UserLogins { get { return Context.Set(); } } + private DbSet> UserClaims { get { return Context.Set>(); } } + private DbSet> UserRoles { get { return Context.Set>(); } } + private DbSet> UserLogins { get { return Context.Set>(); } } public Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) { @@ -367,7 +395,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("user"); } - var result = UserClaims.Where(uc => uc.UserId == user.Id).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + var result = UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); return Task.FromResult((IList)result); } @@ -382,7 +410,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("claim"); } - UserClaims.Add(new IdentityUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); + UserClaims.Add(new IdentityUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); return Task.FromResult(0); } @@ -417,7 +445,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("login"); } - var l = new IdentityUserLogin + var l = new IdentityUserLogin { UserId = user.Id, ProviderKey = login.ProviderKey, @@ -445,7 +473,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework var key = login.ProviderKey; var userId = user.Id; // todo: ensure logins loaded - var entry = UserLogins.SingleOrDefault(l => l.UserId == userId && l.LoginProvider == provider && l.ProviderKey == key); + var entry = UserLogins.SingleOrDefault(l => l.UserId.Equals(userId) && l.LoginProvider == provider && l.ProviderKey == key); if (entry != null) { UserLogins.Remove(entry); @@ -591,9 +619,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework throw new ArgumentNullException("user"); } return - Task.FromResult(user.LockoutEnd.HasValue - ? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEnd.Value, DateTimeKind.Utc)) - : new DateTimeOffset()); + Task.FromResult(user.LockoutEnd); } /// @@ -611,7 +637,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework { throw new ArgumentNullException("user"); } - user.LockoutEnd = lockoutEnd == DateTimeOffset.MinValue ? (DateTime?)null : lockoutEnd.UtcDateTime; + user.LockoutEnd = lockoutEnd; return Task.FromResult(0); } diff --git a/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs b/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs index 1d3deac288..a76c130aa2 100644 --- a/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs +++ b/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs @@ -41,22 +41,26 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CreateAsync(TUser user, - string authenticationType, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task CreateAsync(TUser user, ClaimsIdentityOptions options, + CancellationToken cancellationToken = default(CancellationToken)) { if (user == null) { throw new ArgumentNullException("user"); } + if (options == null) + { + throw new ArgumentNullException("options"); + } var userId = await UserManager.GetUserIdAsync(user, cancellationToken); var userName = await UserManager.GetUserNameAsync(user, cancellationToken); - var id = new ClaimsIdentity(authenticationType, UserManager.Options.ClaimType.UserName, - UserManager.Options.ClaimType.Role); - id.AddClaim(new Claim(UserManager.Options.ClaimType.UserId, userId)); - id.AddClaim(new Claim(UserManager.Options.ClaimType.UserName, userName, ClaimValueTypes.String)); + var id = new ClaimsIdentity(options.AuthenticationType, options.UserNameClaimType, + options.RoleClaimType); + id.AddClaim(new Claim(options.UserIdClaimType, userId)); + id.AddClaim(new Claim(options.UserNameClaimType, userName, ClaimValueTypes.String)); if (UserManager.SupportsUserSecurityStamp) { - id.AddClaim(new Claim(UserManager.Options.ClaimType.SecurityStamp, + id.AddClaim(new Claim(options.SecurityStampClaimType, await UserManager.GetSecurityStampAsync(user, cancellationToken))); } if (UserManager.SupportsUserRole) @@ -64,7 +68,7 @@ namespace Microsoft.AspNet.Identity var roles = await UserManager.GetRolesAsync(user, cancellationToken); foreach (var roleName in roles) { - id.AddClaim(new Claim(UserManager.Options.ClaimType.Role, roleName, ClaimValueTypes.String)); + id.AddClaim(new Claim(options.RoleClaimType, roleName, ClaimValueTypes.String)); if (RoleManager.SupportsRoleClaims) { var role = await RoleManager.FindByNameAsync(roleName); diff --git a/src/Microsoft.AspNet.Identity/ClaimsIdentityOptions.cs b/src/Microsoft.AspNet.Identity/ClaimsIdentityOptions.cs new file mode 100644 index 0000000000..eb3ac935ea --- /dev/null +++ b/src/Microsoft.AspNet.Identity/ClaimsIdentityOptions.cs @@ -0,0 +1,46 @@ +// 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; + +namespace Microsoft.AspNet.Identity +{ + public class ClaimsIdentityOptions + { + /// + /// ClaimType used for the security stamp by default + /// + public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp"; + + public ClaimsIdentityOptions() + { + AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie; + RoleClaimType = ClaimTypes.Role; + SecurityStampClaimType = DefaultSecurityStampClaimType; + UserIdClaimType = ClaimTypes.NameIdentifier; + UserNameClaimType = ClaimTypes.Name; + } + + public string AuthenticationType { get; set; } + + /// + /// Claim type used for role claims + /// + public string RoleClaimType { get; set; } + + /// + /// Claim type used for the user name + /// + public string UserNameClaimType { get; set; } + + /// + /// Claim type used for the user id + /// + public string UserIdClaimType { get; set; } + + /// + /// Claim type used for the user security stamp + /// + public string SecurityStampClaimType { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs b/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs index 6d87f05a42..9be8991d47 100644 --- a/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs +++ b/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task CreateAsync(TUser user, string authenticationType, + Task CreateAsync(TUser user, ClaimsIdentityOptions options, CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityOptions.cs b/src/Microsoft.AspNet.Identity/IdentityOptions.cs index 1a703d0186..18ca66c9c4 100644 --- a/src/Microsoft.AspNet.Identity/IdentityOptions.cs +++ b/src/Microsoft.AspNet.Identity/IdentityOptions.cs @@ -10,13 +10,13 @@ namespace Microsoft.AspNet.Identity { public IdentityOptions() { - ClaimType = new ClaimTypeOptions(); + ClaimsIdentity = new ClaimsIdentityOptions(); User = new UserOptions(); Password = new PasswordOptions(); Lockout = new LockoutOptions(); } - public ClaimTypeOptions ClaimType { get; set; } + public ClaimsIdentityOptions ClaimsIdentity { get; set; } public UserOptions User { get; set; } diff --git a/src/Microsoft.AspNet.Identity/IdentityRole.cs b/src/Microsoft.AspNet.Identity/IdentityRole.cs index 8c20bcd996..0a2206a989 100644 --- a/src/Microsoft.AspNet.Identity/IdentityRole.cs +++ b/src/Microsoft.AspNet.Identity/IdentityRole.cs @@ -23,8 +23,7 @@ namespace Microsoft.AspNet.Identity /// Constructor /// /// - public IdentityRole(string roleName) - : this() + public IdentityRole(string roleName) : this() { Name = roleName; } @@ -45,6 +44,16 @@ namespace Microsoft.AspNet.Identity Claims = new List>(); } + /// + /// Constructor + /// + /// + public IdentityRole(string roleName) + : this() + { + Name = roleName; + } + /// /// Navigation property for users in the role /// diff --git a/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs index ecb064dfe2..b09e4e6e1e 100644 --- a/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs @@ -43,26 +43,6 @@ namespace Microsoft.Framework.DependencyInjection return new IdentityBuilder(services); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, - Action> actionBuilder) - where TUser : class - where TRole : class - { - var builder = services.AddIdentity(); - actionBuilder(builder); - return builder; - } - - public static IdentityBuilder AddIdentity(this ServiceCollection services, - IConfiguration identityConfig, Action> actionBuilder) - where TUser : class - where TRole : class - { - var builder = services.AddIdentity(identityConfig); - actionBuilder(builder); - return builder; - } - public static IdentityBuilder AddIdentity(this ServiceCollection services) where TUser : class { @@ -75,19 +55,5 @@ namespace Microsoft.Framework.DependencyInjection { return services.AddIdentity(identityConfig); } - - public static IdentityBuilder AddIdentity(this ServiceCollection services, - Action> actionBuilder) - where TUser : class - { - return services.AddIdentity(actionBuilder); - } - - public static IdentityBuilder AddIdentity(this ServiceCollection services, - IConfiguration identityConfig, Action> actionBuilder) - where TUser : class - { - return services.AddIdentity(identityConfig, actionBuilder); - } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityUser.cs b/src/Microsoft.AspNet.Identity/IdentityUser.cs index 4951c5e902..b8b1be1bf1 100644 --- a/src/Microsoft.AspNet.Identity/IdentityUser.cs +++ b/src/Microsoft.AspNet.Identity/IdentityUser.cs @@ -29,6 +29,11 @@ namespace Microsoft.AspNet.Identity Logins = new List>(); } + public IdentityUser(string userName) : this() + { + UserName = userName; + } + public virtual TKey Id { get; set; } public virtual string UserName { get; set; } diff --git a/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj b/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj index 9d8451cbe3..27285c8215 100644 --- a/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj +++ b/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj @@ -22,7 +22,7 @@ - + diff --git a/src/Microsoft.AspNet.Identity/Resources.resx b/src/Microsoft.AspNet.Identity/Resources.resx index 5eacf05da9..d7f2489732 100644 --- a/src/Microsoft.AspNet.Identity/Resources.resx +++ b/src/Microsoft.AspNet.Identity/Resources.resx @@ -170,7 +170,7 @@ Error when passwords do not have a lowercase letter - Passwords must have at least one non letter or digit character. + Passwords must have at least one non letter and non digit character. Error when password does not have enough letter or digit characters diff --git a/src/Microsoft.AspNet.Identity/SignInManager.cs b/src/Microsoft.AspNet.Identity/SignInManager.cs index 61c3122818..be9bcd564b 100644 --- a/src/Microsoft.AspNet.Identity/SignInManager.cs +++ b/src/Microsoft.AspNet.Identity/SignInManager.cs @@ -31,12 +31,8 @@ namespace Microsoft.AspNet.Identity UserManager = userManager; AuthenticationManager = authenticationManager; ClaimsFactory = claimsFactory; - AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie; } - // TODO: this should go into some kind of Options/setup - public string AuthenticationType { get; set; } - public UserManager UserManager { get; private set; } public IAuthenticationManager AuthenticationManager { get; private set; } public IClaimsIdentityFactory ClaimsFactory { get; private set; } @@ -44,7 +40,8 @@ namespace Microsoft.AspNet.Identity // Should this be a func? public virtual async Task CreateUserIdentityAsync(TUser user) { - return await ClaimsFactory.CreateAsync(user, AuthenticationType); + // REVIEW: should sign in manager take options instead of using the user manager instance? + return await ClaimsFactory.CreateAsync(user, UserManager.Options.ClaimsIdentity); } public virtual async Task SignInAsync(TUser user, bool isPersistent) @@ -56,7 +53,8 @@ namespace Microsoft.AspNet.Identity // TODO: Should this be async? public void SignOut() { - AuthenticationManager.SignOut(AuthenticationType); + // REVIEW: need a new home for this option config? + AuthenticationManager.SignOut(UserManager.Options.ClaimsIdentity.AuthenticationType); } public virtual async Task PasswordSignInAsync(string userName, string password, diff --git a/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs b/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs index 3c850dbc1a..f82391ca06 100644 --- a/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs +++ b/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs @@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test contextAccessor.Setup(a => a.Value).Returns(context.Object); var roleManager = MockHelpers.MockRoleManager(); var claimsFactory = new Mock>(manager.Object, roleManager.Object); - claimsFactory.Setup(m => m.CreateAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable(); + claimsFactory.Setup(m => m.CreateAsync(user, manager.Object.Options.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable(); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act @@ -321,7 +321,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test var signInService = new HttpAuthenticationManager(contextAccessor.Object); var roleManager = MockHelpers.MockRoleManager(); var claimsFactory = new Mock>(manager.Object, roleManager.Object); - claimsFactory.Setup(m => m.CreateAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable(); + claimsFactory.Setup(m => m.CreateAsync(user, manager.Object.Options.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable(); var helper = new SignInManager(manager.Object, signInService, claimsFactory.Object); // Act @@ -351,10 +351,8 @@ namespace Microsoft.AspNet.Identity.Authentication.Test contextAccessor.Setup(a => a.Value).Returns(context.Object); var roleManager = MockHelpers.MockRoleManager(); var claimsFactory = new Mock>(manager.Object, roleManager.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object) - { - AuthenticationType = authenticationType - }; + manager.Object.Options.ClaimsIdentity.AuthenticationType = authenticationType; + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act helper.SignOut(); diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityInMemoryTestServiceCollectionExtensions.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityInMemoryTestServiceCollectionExtensions.cs new file mode 100644 index 0000000000..90d59829bb --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityInMemoryTestServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +// 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.EntityFramework; +using Microsoft.AspNet.Identity.EntityFramework.InMemory.Test; +using Microsoft.Data.Entity; +using Microsoft.Framework.DependencyInjection; + +namespace Microsoft.AspNet.Identity +{ + public static class EntityInMemoryTestServiceCollectionExtensions + { + public static IdentityBuilder AddIdentityInMemory(this ServiceCollection services) + where TUser : InMemoryUser + where TRole : IdentityRole + where TDbContext : DbContext + { + var builder = services.AddIdentity(); + services.AddScoped(); + services.AddScoped, InMemoryUserStore>(); + services.AddScoped, RoleStore>(); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs index 61665e8503..ecaecafc5f 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs @@ -72,7 +72,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) .ToTable("AspNetUserClaims"); - builder.Entity() + builder.Entity>() .Key(c => c.Id) .ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)) .ToTable("AspNetRoleClaims"); diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs index 13c68de6f8..70893ba36b 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs @@ -44,10 +44,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test // TODO: this needs to construct a new instance of InMemoryStore var store = new InMemoryUserStore(new InMemoryContext()); services.Add(OptionsServices.GetDefaultServices()); - services.AddIdentity(s => - { - s.AddUserStore(() => store); - }); + services.AddIdentity().AddUserStore(() => store); var provider = services.BuildServiceProvider(); var manager = provider.GetService>(); @@ -585,14 +582,15 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test } var claimsFactory = new ClaimsIdentityFactory(manager, role); - var identity = await claimsFactory.CreateAsync(user, "test"); + var identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); var claims = identity.Claims.ToList(); Assert.NotNull(claims); Assert.True( - 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")); + claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id.ToString())); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local")); foreach (var cl in userClaims) { Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj index 399135e972..265d77ccaa 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj @@ -21,7 +21,7 @@ - + diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs index 5c216147c4..f1e98bb85d 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs @@ -18,10 +18,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test var services = new ServiceCollection(); services.AddEntityFramework().AddInMemoryStore(); var store = new RoleStore(new InMemoryContext()); - services.AddIdentity(s => - { - s.AddRoleStore(() => store); - }); + services.AddIdentity().AddRoleStore(() => store); var provider = services.BuildServiceProvider(); var manager = provider.GetService>(); Assert.NotNull(manager); diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs index 83b94b677e..3591b3e724 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test public static RoleManager CreateRoleManager(InMemoryContext context) { var services = new ServiceCollection(); - services.AddIdentity(b => b.AddRoleStore(() => new RoleStore(context))); + services.AddIdentity().AddRoleStore(() => new RoleStore(context)); return services.BuildServiceProvider().GetService>(); } diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/CustomPocoTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/CustomPocoTest.cs new file mode 100644 index 0000000000..6c75f6d483 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/CustomPocoTest.cs @@ -0,0 +1,160 @@ +// 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.Data.Entity; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + public class CustomPocoTest + { + private const string ConnectionString = @"Server=(localdb)\v11.0;Database=CustomPocoTest;Trusted_Connection=True;"; + + public class User where TKey : IEquatable + { + public TKey Id { get; set; } + public string UserName { get; set; } + } + + public class CustomDbContext : DbContext where TUser : class + //where TUser : User where TKey : IEquatable + { + public DbSet Users { get; set; } + + public CustomDbContext(IServiceProvider services) : base(services) { } + + protected override void OnConfiguring(DbContextOptions builder) + { + builder.UseSqlServer(ConnectionString); + } + + //protected override void OnModelCreating(ModelBuilder builder) + //{ + // builder.Entity() + // .Key(u => u.Id) + // .Properties(ps => ps.Property(u => u.UserName)); + //} + } + + //public static CustomDbContext, TKey> CreateContext(bool delete = false) where TKey : IEquatable + public static CustomDbContext CreateContext(bool delete = false) where TUser : class + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddSqlServer(); + var serviceProvider = services.BuildServiceProvider(); + + var db = new CustomDbContext(serviceProvider); + if (delete) + { + db.Database.EnsureDeleted(); + } + db.Database.EnsureCreated(); + return db; + } + + [Fact] + public async Task CanUpdateNameGuid() + { + using (var db = CreateContext>(true)) + { + var oldName = Guid.NewGuid().ToString(); + var user = new User { UserName = oldName, Id = Guid.NewGuid() }; + db.Users.Add(user); + await db.SaveChangesAsync(); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + await db.SaveChangesAsync(); + Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); + Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + } + } + + [Fact] + public async Task CanUpdateNameString() + { + using (var db = CreateContext>(true)) + { + var oldName = Guid.NewGuid().ToString(); + var user = new User { UserName = oldName, Id = Guid.NewGuid().ToString() }; + db.Users.Add(user); + await db.SaveChangesAsync(); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + await db.SaveChangesAsync(); + Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); + Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + } + } + + [Fact] + public async Task CanCreateUserInt() + { + using (var db = CreateContext>(true)) + { + var user = new User { Id = 11 }; + db.Users.Add(user); + await db.SaveChangesAsync(); + user.UserName = "Boo"; + await db.SaveChangesAsync(); + var fetch = db.Users.First(u => u.UserName == "Boo"); + Assert.Equal(user, fetch); + } + } + + [Fact] + public async Task CanCreateUserIntViaSet() + { + using (var db = CreateContext>(true)) + { + var user = new User { Id = 11 }; + var users = db.Set>(); + users.Add(user); + await db.SaveChangesAsync(); + user.UserName = "Boo"; + await db.SaveChangesAsync(); + var fetch = users.First(u => u.UserName == "Boo"); + Assert.Equal(user, fetch); + } + } + + [Fact] + public async Task CanUpdateNameInt() + { + using (var db = CreateContext>(true)) + { + var oldName = Guid.NewGuid().ToString(); + var user = new User { UserName = oldName, Id = 1 }; + db.Users.Add(user); + await db.SaveChangesAsync(); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + await db.SaveChangesAsync(); + Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); + Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + } + } + + [Fact] + public async Task CanUpdateNameIntWithSet() + { + using (var db = CreateContext>(true)) + { + var oldName = Guid.NewGuid().ToString(); + var user = new User { UserName = oldName, Id = 1 }; + db.Set>().Add(user); + await db.SaveChangesAsync(); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + await db.SaveChangesAsync(); + Assert.Null(db.Set>().SingleOrDefault(u => u.UserName == oldName)); + Assert.Equal(user, db.Set>().Single(u => u.UserName == newName)); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/DefaultPocoTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/DefaultPocoTest.cs new file mode 100644 index 0000000000..911f941313 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/DefaultPocoTest.cs @@ -0,0 +1,66 @@ +// 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.Builder; +using Microsoft.AspNet.Identity.Test; +using Microsoft.Data.Entity; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + public class DefaultPocoTest + { + private const string ConnectionString = @"Server=(localdb)\v11.0;Database=DefaultPocoTest;Trusted_Connection=True;"; + public static IdentityDbContext CreateContext(bool delete = false) + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddSqlServer(); + var serviceProvider = services.BuildServiceProvider(); + + var db = new IdentityDbContext(serviceProvider, ConnectionString); + if (delete) + { + db.Database.EnsureDeleted(); + } + db.Database.EnsureCreated(); + return db; + } + + public static void EnsureDatabase() + { + CreateContext(); + } + + [Fact] + public async Task EnsureStartupUsageWorks() + { + var context = CreateContext(true); + IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); + + builder.UseServices(services => + { + services.AddEntityFramework().AddSqlServer(); + services.AddIdentitySqlServer(); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + // todo: constructor resolution doesn't work well with IdentityDbContext since it has 4 constructors + services.AddInstance(context); + }); + + var userStore = builder.ApplicationServices.GetService>(); + var userManager = builder.ApplicationServices.GetService>(); + + Assert.NotNull(userStore); + Assert.NotNull(userManager); + + const string userName = "admin"; + const string password = "1qaz@WSX"; + var user = new IdentityUser { UserName = userName }; + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); + IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj b/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj index b7c20385e7..0ef5cfe2f8 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj @@ -21,7 +21,13 @@ + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreGuidKeyTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreGuidKeyTest.cs new file mode 100644 index 0000000000..51b3874c96 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreGuidKeyTest.cs @@ -0,0 +1,73 @@ +// 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.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using System; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + public class GuidUser : IdentityUser + { + public GuidUser() + { + Id = Guid.NewGuid(); + UserName = Id.ToString(); + } + } + + public class GuidRole : IdentityRole + { + public GuidRole() + { + Id = Guid.NewGuid(); + Name = Id.ToString(); + } + } + + [TestCaseOrderer("Microsoft.AspNet.Identity.Test.PriorityOrderer", "Microsoft.AspNet.Identity.EntityFramework.Test")] + public class UserStoreGuidTest : UserStoreTestBase + { + public override string ConnectionString + { + get + { + return @"Server=(localdb)\v11.0;Database=SqlUserStoreGuidTest;Trusted_Connection=True;"; + } + } + + public class ApplicationUserStore : UserStore + { + public ApplicationUserStore(ApplicationDbContext context) : base(context) { } + + public override Guid ConvertIdFromString(string userId) + { + return new Guid(userId); + } + } + + public class ApplicationRoleStore : RoleStore + { + public ApplicationRoleStore(ApplicationDbContext context) : base(context) { } + + public override Guid ConvertIdFromString(string id) + { + return new Guid(id); + } + } + + public override UserManager CreateManager(ApplicationDbContext context) + { + return MockHelpers.CreateManager(() => new ApplicationUserStore(context)); + } + + public override RoleManager CreateRoleManager(ApplicationDbContext context) + { + var services = new ServiceCollection(); + services.AddIdentity().AddRoleStore(() => new ApplicationRoleStore(context)); + return services.BuildServiceProvider().GetService>(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreIntKeyTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreIntKeyTest.cs new file mode 100644 index 0000000000..09b9c82885 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreIntKeyTest.cs @@ -0,0 +1,51 @@ +// 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.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using System; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + public class IntUser : IdentityUser + { + public IntUser() + { + UserName = Guid.NewGuid().ToString(); + } + } + + public class IntRole : IdentityRole + { + public IntRole() + { + Name = Guid.NewGuid().ToString(); + } + } + + [TestCaseOrderer("Microsoft.AspNet.Identity.Test.PriorityOrderer", "Microsoft.AspNet.Identity.EntityFramework.Test")] + public class UserStoreIntTest : UserStoreTestBase + { + public override string ConnectionString + { + get + { + return @"Server=(localdb)\v11.0;Database=SqlUserStoreIntTest;Trusted_Connection=True;"; + } + } + + public override UserManager CreateManager(ApplicationDbContext context) + { + return MockHelpers.CreateManager(() => new UserStore(context)); + } + + public override RoleManager CreateRoleManager(ApplicationDbContext context) + { + var services = new ServiceCollection(); + services.AddIdentity().AddRoleStore(() => new RoleStore(context)); + return services.BuildServiceProvider().GetService>(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreStringKeyTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreStringKeyTest.cs new file mode 100644 index 0000000000..41522b455f --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreStringKeyTest.cs @@ -0,0 +1,54 @@ +// 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.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using System; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + [TestCaseOrderer("Microsoft.AspNet.Identity.Test.PriorityOrderer", "Microsoft.AspNet.Identity.EntityFramework.Test")] + public class StringUser : IdentityUser + { + public StringUser() + { + Id = Guid.NewGuid().ToString(); + UserName = Id; + } + } + + public class StringRole : IdentityRole + { + public StringRole() + { + Id = Guid.NewGuid().ToString(); + Name = Id; + } + } + + [TestCaseOrderer("Microsoft.AspNet.Identity.Test.PriorityOrderer", "Microsoft.AspNet.Identity.EntityFramework.Test")] + public class UserStoreStringKeyTest : UserStoreTestBase + { + public override string ConnectionString + { + get + { + return @"Server=(localdb)\v11.0;Database=SqlUserStoreStringTest;Trusted_Connection=True;"; + } + } + + public override UserManager CreateManager(ApplicationDbContext context) + { + return MockHelpers.CreateManager(() => new UserStore(context)); + } + + public override RoleManager CreateRoleManager(ApplicationDbContext context) + { + var services = new ServiceCollection(); + services.AddIdentity().AddRoleStore(() => new RoleStore(context)); + return services.BuildServiceProvider().GetService>(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs index ec9878273c..5814656781 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { private const string ConnectionString = @"Server=(localdb)\v11.0;Database=SqlUserStoreTest;Trusted_Connection=True;"; - public class ApplicationUser : User { } + public class ApplicationUser : IdentityUser { } public class ApplicationDbContext : IdentityDbContext { @@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test builder.UseServices(services => { services.AddEntityFramework().AddSqlServer(); - services.AddIdentity().AddEntityFramework(); + services.AddIdentitySqlServer(); services.SetupOptions(options => options.UseSqlServer(ConnectionString)); }); @@ -64,13 +64,47 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); } + [Fact] + public async Task EnsureStartupOptionsChangeWorks() + { + EnsureDatabase(); + IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); + + builder.UseServices(services => + { + services.AddEntityFramework().AddSqlServer(); + services.AddIdentitySqlServer().SetupOptions(options => + { + options.Password.RequiredLength = 1; + options.Password.RequireLowercase = false; + options.Password.RequireNonLetterOrDigit = false; + options.Password.RequireUppercase = false; + options.Password.RequireDigit = false; + }); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + }); + + var userStore = builder.ApplicationServices.GetService>(); + var userManager = builder.ApplicationServices.GetService>(); + + Assert.NotNull(userStore); + Assert.NotNull(userManager); + + const string userName = "admin"; + const string password = "a"; + var user = new ApplicationUser { UserName = userName }; + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); + IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); + } + [Fact] public void CanCreateUserUsingEF() { using (var db = CreateContext()) { var guid = Guid.NewGuid().ToString(); - db.Users.Add(new User {Id = guid, UserName = guid}); + db.Users.Add(new IdentityUser {Id = guid, UserName = guid}); db.SaveChanges(); Assert.True(db.Users.Any(u => u.UserName == guid)); Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid)); @@ -110,12 +144,12 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test return db; } - public static UserManager CreateManager(DbContext context) + public static UserManager CreateManager(DbContext context) { - return MockHelpers.CreateManager(() => new UserStore(context)); + return MockHelpers.CreateManager(() => new UserStore(context)); } - public static UserManager CreateManager() + public static UserManager CreateManager() { return CreateManager(CreateContext()); } @@ -123,7 +157,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public static RoleManager CreateRoleManager(IdentityDbContext context) { var services = new ServiceCollection(); - services.AddIdentity(b => b.AddRoleStore(() => new RoleStore(context))); + services.AddIdentity().AddRoleStore(() => new RoleStore(context)); return services.BuildServiceProvider().GetService>(); } @@ -196,13 +230,13 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test await Assert.ThrowsAsync("user", async () => await store.SetSecurityStampAsync(null, null)); await Assert.ThrowsAsync("claim", - async () => await store.AddClaimAsync(new User("fake"), null)); + async () => await store.AddClaimAsync(new IdentityUser("fake"), null)); await Assert.ThrowsAsync("claim", - async () => await store.RemoveClaimAsync(new User("fake"), null)); + async () => await store.RemoveClaimAsync(new IdentityUser("fake"), null)); await Assert.ThrowsAsync("login", - async () => await store.AddLoginAsync(new User("fake"), null)); + async () => await store.AddLoginAsync(new IdentityUser("fake"), null)); await Assert.ThrowsAsync("login", - async () => await store.RemoveLoginAsync(new User("fake"), null)); + async () => await store.RemoveLoginAsync(new IdentityUser("fake"), null)); await Assert.ThrowsAsync("login", async () => await store.FindByLoginAsync(null)); await Assert.ThrowsAsync("user", async () => await store.GetEmailConfirmedAsync(null)); await Assert.ThrowsAsync("user", @@ -225,12 +259,12 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test await Assert.ThrowsAsync("user", async () => await store.SetLockoutEndDateAsync(null, new DateTimeOffset())); await Assert.ThrowsAsync("user", async () => await store.ResetAccessFailedCountAsync(null)); await Assert.ThrowsAsync("user", async () => await store.IncrementAccessFailedCountAsync(null)); - await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new User("fake"), null)); - await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new User("fake"), null)); - await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new User("fake"), null)); - await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new User("fake"), "")); - await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new User("fake"), "")); - await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new User("fake"), "")); + await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new IdentityUser("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new IdentityUser("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new IdentityUser("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new IdentityUser("fake"), "")); + await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new IdentityUser("fake"), "")); + await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new IdentityUser("fake"), "")); } [Fact] @@ -238,7 +272,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); var guid = Guid.NewGuid().ToString(); - var user = new User { UserName = "New"+guid }; + var user = new IdentityUser { UserName = "New"+guid }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); } @@ -247,7 +281,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanDeleteUser() { var manager = CreateManager(); - var user = new User("CanDeleteUser"); + var user = new IdentityUser("CanDeleteUser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); Assert.Null(await manager.FindByIdAsync(user.Id)); @@ -258,7 +292,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); var oldName = Guid.NewGuid().ToString(); - var user = new User(oldName); + var user = new IdentityUser(oldName); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var newName = Guid.NewGuid().ToString(); user.UserName = newName; @@ -272,7 +306,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanUpdatePasswordUsingHasher() { var manager = CreateManager(); - var user = new User("CanUpdatePasswordUsingHasher"); + var user = new IdentityUser("CanUpdatePasswordUsingHasher"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); Assert.True(await manager.CheckPasswordAsync(user, "password")); user.PasswordHash = manager.PasswordHasher.HashPassword("New"); @@ -287,7 +321,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); var oldName = Guid.NewGuid().ToString(); - var user = new User(oldName); + var user = new IdentityUser(oldName); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var newName = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName)); @@ -300,7 +334,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task RemoveClaimOnlyForUser() { var manager = CreateManager(); - var user = new User("RemoveClaimForMe"); + var user = new IdentityUser("RemoveClaimForMe"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); } @@ -309,7 +343,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task UserValidatorCanBlockCreate() { var manager = CreateManager(); - var user = new User("UserValidatorCanBlockCreate"); + var user = new IdentityUser("UserValidatorCanBlockCreate"); manager.UserValidator = new AlwaysBadValidator(); IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage); } @@ -318,7 +352,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task UserValidatorCanBlockUpdate() { var manager = CreateManager(); - var user = new User("UserValidatorCanBlockUpdate"); + var user = new IdentityUser("UserValidatorCanBlockUpdate"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); manager.UserValidator = new AlwaysBadValidator(); IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage); @@ -330,7 +364,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) { var manager = CreateManager(); - var user = new User("UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail") { Email = email }; + var user = new IdentityUser("UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail") { Email = email }; manager.Options.User.RequireUniqueEmail = true; IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty."); } @@ -342,7 +376,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) { var manager = CreateManager(); - var user = new User("UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail") { Email = email }; + var user = new IdentityUser("UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail") { Email = email }; manager.Options.User.RequireUniqueEmail = true; IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid."); } @@ -352,7 +386,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task PasswordValidatorCanBlockAddPassword() { var manager = CreateManager(); - var user = new User("AddPasswordBlocked"); + var user = new IdentityUser("AddPasswordBlocked"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); manager.PasswordValidator = new AlwaysBadValidator(); IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), @@ -363,7 +397,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task PasswordValidatorCanBlockChangePassword() { var manager = CreateManager(); - var user = new User("ChangePasswordBlocked"); + var user = new IdentityUser("ChangePasswordBlocked"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); manager.PasswordValidator = new AlwaysBadValidator(); IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"), @@ -374,7 +408,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanCreateUserNoPassword() { var manager = CreateManager(); - IdentityResultAssert.IsSuccess(await manager.CreateAsync(new User("CanCreateUserNoPassword"))); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new IdentityUser("CanCreateUserNoPassword"))); var user = await manager.FindByNameAsync("CanCreateUserNoPassword"); Assert.NotNull(user); Assert.Null(user.PasswordHash); @@ -390,7 +424,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test const string userName = "CanCreateUserAddLogin"; const string provider = "ZzAuth"; const string providerKey = "HaoKey"; - IdentityResultAssert.IsSuccess(await manager.CreateAsync(new User(userName))); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new IdentityUser(userName))); var user = await manager.FindByNameAsync(userName); Assert.NotNull(user); var login = new UserLoginInfo(provider, providerKey); @@ -407,7 +441,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); var login = new UserLoginInfo("Provider", "key"); - var user = new User("CreateUserLoginAddPasswordTest"); + var user = new IdentityUser("CreateUserLoginAddPasswordTest"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); Assert.False(await manager.HasPasswordAsync(user)); @@ -424,7 +458,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task AddPasswordFailsIfAlreadyHave() { var manager = CreateManager(); - var user = new User("CannotAddAnotherPassword"); + var user = new IdentityUser("CannotAddAnotherPassword"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); Assert.True(await manager.HasPasswordAsync(user)); IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), @@ -435,7 +469,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanCreateUserAddRemoveLogin() { var manager = CreateManager(); - var user = new User("CreateUserAddRemoveLoginTest"); + var user = new IdentityUser("CreateUserAddRemoveLoginTest"); var login = new UserLoginInfo("Provider", "key"); var result = await manager.CreateAsync(user); Assert.NotNull(user); @@ -460,7 +494,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanRemovePassword() { var manager = CreateManager(); - var user = new User("RemovePasswordTest"); + var user = new IdentityUser("RemovePasswordTest"); const string password = "password"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); var stamp = user.SecurityStamp; @@ -475,7 +509,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanChangePassword() { var manager = CreateManager(); - var user = new User("ChangePasswordTest"); + var user = new IdentityUser("ChangePasswordTest"); const string password = "password"; const string newPassword = "newpassword"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); @@ -491,7 +525,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task CanAddRemoveUserClaim() { var manager = CreateManager(); - var user = new User("ClaimsAddRemove"); + var user = new IdentityUser("ClaimsAddRemove"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; foreach (var c in claims) @@ -516,7 +550,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task ChangePasswordFallsIfPasswordWrong() { var manager = CreateManager(); - var user = new User("user"); + var user = new IdentityUser("user"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword"); IdentityResultAssert.IsFailure(result, "Incorrect password."); @@ -526,8 +560,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task AddDupeUserNameFails() { var manager = CreateManager(); - var user = new User("AddDupeUserNameFails"); - var user2 = new User("AddDupeUserNameFails"); + var user = new IdentityUser("AddDupeUserNameFails"); + var user2 = new IdentityUser("AddDupeUserNameFails"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Name AddDupeUserNameFails is already taken."); } @@ -536,8 +570,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task AddDupeEmailAllowedByDefault() { var manager = CreateManager(); - var user = new User("AddDupeEmailAllowedByDefault") { Email = "yup@yup.com" }; - var user2 = new User("AddDupeEmailAllowedByDefault2") { Email = "yup@yup.com" }; + var user = new IdentityUser("AddDupeEmailAllowedByDefault") { Email = "yup@yup.com" }; + var user2 = new IdentityUser("AddDupeEmailAllowedByDefault2") { Email = "yup@yup.com" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); } @@ -547,8 +581,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.Options.User.RequireUniqueEmail = true; - var user = new User("AddDupeEmailFallsWhenUniqueEmailRequired") { Email = "dupeEmailTrue@yup.com" }; - var user2 = new User("AddDupeEmailFallsWhenUniqueEmailRequiredDupe") { Email = "dupeEmailTrue@yup.com" }; + var user = new IdentityUser("AddDupeEmailFallsWhenUniqueEmailRequired") { Email = "dupeEmailTrue@yup.com" }; + var user2 = new IdentityUser("AddDupeEmailFallsWhenUniqueEmailRequiredDupe") { Email = "dupeEmailTrue@yup.com" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Email 'dupeEmailTrue@yup.com' is already taken."); } @@ -557,7 +591,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task UpdateSecurityStampActuallyChanges() { var manager = CreateManager(); - var user = new User("UpdateSecurityStampActuallyChanges"); + var user = new IdentityUser("UpdateSecurityStampActuallyChanges"); Assert.Null(user.SecurityStamp); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var stamp = user.SecurityStamp; @@ -570,7 +604,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task AddDupeLoginFails() { var manager = CreateManager(); - var user = new User("DupeLogin"); + var user = new IdentityUser("DupeLogin"); var login = new UserLoginInfo("provder", "key"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); @@ -585,7 +619,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); const string userName = "EmailTest"; const string email = "email@test.com"; - var user = new User(userName) { Email = email }; + var user = new IdentityUser(userName) { Email = email }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var fetch = await manager.FindByEmailAsync(email); Assert.Equal(user, fetch); @@ -622,7 +656,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var context = CreateContext(); var manager = CreateManager(context); var role = CreateRoleManager(context); - var user = new User("ClaimsIdentityCreatesExpectedClaims"); + var user = new IdentityUser("ClaimsIdentityCreatesExpectedClaims"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var admin = new IdentityRole("Admin"); var local = new IdentityRole("Local"); @@ -657,15 +691,16 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test IdentityResultAssert.IsSuccess(await role.AddClaimAsync(local, c)); } - var claimsFactory = new ClaimsIdentityFactory(manager, role); - var identity = await claimsFactory.CreateAsync(user, "test"); + var claimsFactory = new ClaimsIdentityFactory(manager, role); + var identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); var claims = identity.Claims.ToList(); Assert.NotNull(claims); Assert.True( - 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")); + claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id.ToString())); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local")); foreach (var cl in userClaims) { Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); @@ -681,7 +716,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test // Remove a role claim and make sure its not there IdentityResultAssert.IsSuccess(await role.RemoveClaimAsync(local, localClaims[0])); - identity = await claimsFactory.CreateAsync(user, "test"); + identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); claims = identity.Claims.ToList(); Assert.False(claims.Any(c => c.Type == localClaims[0].Type && c.Value == localClaims[0].Value)); Assert.True(claims.Any(c => c.Type == localClaims[1].Type && c.Value == localClaims[1].Value)); @@ -691,37 +727,37 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task ConfirmEmailFalseByDefaultTest() { var manager = CreateManager(); - var user = new User("ConfirmEmailFalseByDefaultTest"); + var user = new IdentityUser("ConfirmEmailFalseByDefaultTest"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Assert.False(await manager.IsEmailConfirmedAsync(user)); } // TODO: No token provider implementations yet - private class StaticTokenProvider : IUserTokenProvider + private class StaticTokenProvider : IUserTokenProvider { - public Task GenerateAsync(string purpose, UserManager manager, - User user, CancellationToken token) + public Task GenerateAsync(string purpose, UserManager manager, + IdentityUser user, CancellationToken token) { return Task.FromResult(MakeToken(purpose, user)); } - public Task ValidateAsync(string purpose, string token, UserManager manager, - User user, CancellationToken cancellationToken) + public Task ValidateAsync(string purpose, string token, UserManager manager, + IdentityUser user, CancellationToken cancellationToken) { return Task.FromResult(token == MakeToken(purpose, user)); } - public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + public Task NotifyAsync(string token, UserManager manager, IdentityUser user, CancellationToken cancellationToken) { return Task.FromResult(0); } - public Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + public Task IsValidProviderForUserAsync(UserManager manager, IdentityUser user, CancellationToken token) { return Task.FromResult(true); } - private static string MakeToken(string purpose, User user) + private static string MakeToken(string purpose, IdentityUser user) { return string.Join(":", user.Id, purpose, "ImmaToken"); } @@ -732,7 +768,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("CanResetPasswordWithStaticTokenProvider"); + var user = new IdentityUser("CanResetPasswordWithStaticTokenProvider"); const string password = "password"; const string newPassword = "newpassword"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); @@ -751,7 +787,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider"); + var user = new IdentityUser("PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider"); const string password = "password"; const string newPassword = "newpassword"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); @@ -772,7 +808,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("ResetPasswordWithStaticTokenProviderFailsWithWrongToken"); + var user = new IdentityUser("ResetPasswordWithStaticTokenProviderFailsWithWrongToken"); const string password = "password"; const string newPassword = "newpassword"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); @@ -789,8 +825,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("CanGenerateAndVerifyUserTokenWithStaticTokenProvider"); - var user2 = new User("CanGenerateAndVerifyUserTokenWithStaticTokenProvider2"); + var user = new IdentityUser("CanGenerateAndVerifyUserTokenWithStaticTokenProvider"); + var user2 = new IdentityUser("CanGenerateAndVerifyUserTokenWithStaticTokenProvider2"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); var token = await manager.GenerateUserTokenAsync("test", user); @@ -805,7 +841,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("CanConfirmEmailWithStaticToken"); + var user = new IdentityUser("CanConfirmEmailWithStaticToken"); Assert.False(user.EmailConfirmed); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var token = await manager.GenerateEmailConfirmationTokenAsync(user); @@ -821,7 +857,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); manager.UserTokenProvider = new StaticTokenProvider(); - var user = new User("ConfirmEmailWithStaticTokenFailsWithWrongToken"); + var user = new IdentityUser("ConfirmEmailWithStaticTokenFailsWithWrongToken"); Assert.False(user.EmailConfirmed); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token."); @@ -833,7 +869,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test //public async Task ConfirmTokenFailsAfterPasswordChange() //{ // var manager = CreateManager(); - // var user = new User("test"); + // var user = new IdentityUser("test"); // Assert.False(user.EmailConfirmed); // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); // var token = await manager.GenerateEmailConfirmationTokenAsync(user); @@ -852,7 +888,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.EnabledByDefault = true; mgr.Options.Lockout.MaxFailedAccessAttempts = 0; - var user = new User("SingleFailureLockout"); + var user = new IdentityUser("SingleFailureLockout"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -870,7 +906,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.EnabledByDefault = true; mgr.Options.Lockout.MaxFailedAccessAttempts = 2; - var user = new User("twoFailureLockout"); + var user = new IdentityUser("twoFailureLockout"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -892,7 +928,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.EnabledByDefault = true; mgr.Options.Lockout.MaxFailedAccessAttempts = 2; - var user = new User("ResetAccessCountPreventsLockout"); + var user = new IdentityUser("ResetAccessCountPreventsLockout"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -917,7 +953,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var mgr = CreateManager(); mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.MaxFailedAccessAttempts = 2; - var user = new User("manualLockout"); + var user = new IdentityUser("manualLockout"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.False(await mgr.GetLockoutEnabledAsync(user)); Assert.False(user.LockoutEnabled); @@ -940,21 +976,20 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var mgr = CreateManager(); mgr.Options.Lockout.EnabledByDefault = true; - var user = new User("UserNotLockedOutWithNullDateTimeAndIsSetToNullDate"); + var user = new IdentityUser("UserNotLockedOutWithNullDateTimeAndIsSetToNullDate"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset())); Assert.False(await mgr.IsLockedOutAsync(user)); Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); - Assert.Null(user.LockoutEnd); } [Fact] public async Task LockoutFailsIfNotEnabled() { var mgr = CreateManager(); - var user = new User("LockoutFailsIfNotEnabled"); + var user = new IdentityUser("LockoutFailsIfNotEnabled"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.False(await mgr.GetLockoutEnabledAsync(user)); Assert.False(user.LockoutEnabled); @@ -968,7 +1003,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var mgr = CreateManager(); mgr.Options.Lockout.EnabledByDefault = true; - var user = new User("LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) }; + var user = new IdentityUser("LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) }; IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -980,7 +1015,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var mgr = CreateManager(); mgr.Options.Lockout.EnabledByDefault = true; - var user = new User("LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut"); + var user = new IdentityUser("LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -993,7 +1028,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var mgr = CreateManager(); mgr.Options.Lockout.EnabledByDefault = true; - var user = new User("LockoutEndToUtcNowPlus5ShouldBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddMinutes(5) }; + var user = new IdentityUser("LockoutEndToUtcNowPlus5ShouldBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddMinutes(5) }; IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -1005,7 +1040,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var mgr = CreateManager(); mgr.Options.Lockout.EnabledByDefault = true; - var user = new User("UserLockedOutWithDateTimeLocalKindNowPlus30"); + var user = new IdentityUser("UserLockedOutWithDateTimeLocalKindNowPlus30"); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); Assert.True(await mgr.GetLockoutEnabledAsync(user)); Assert.True(user.LockoutEnabled); @@ -1027,12 +1062,12 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test Assert.True(await manager.RoleExistsAsync(role.Name)); } - private class AlwaysBadValidator : IUserValidator, IRoleValidator, - IPasswordValidator + private class AlwaysBadValidator : IUserValidator, IRoleValidator, + IPasswordValidator { public const string ErrorMessage = "I'm Bad."; - public Task ValidateAsync(string password, UserManager manager, CancellationToken token) + public Task ValidateAsync(string password, UserManager manager, CancellationToken token) { return Task.FromResult(IdentityResult.Failed(ErrorMessage)); } @@ -1042,7 +1077,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test return Task.FromResult(IdentityResult.Failed(ErrorMessage)); } - public Task ValidateAsync(UserManager manager, User user, CancellationToken token) + public Task ValidateAsync(UserManager manager, IdentityUser user, CancellationToken token) { return Task.FromResult(IdentityResult.Failed(ErrorMessage)); } @@ -1137,7 +1172,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var role = new IdentityRole("deleteNonEmpty"); Assert.False(await roleMgr.RoleExistsAsync(role.Name)); IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); - var user = new User("t"); + var user = new IdentityUser("t"); IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); var roles = await userMgr.GetRolesAsync(user); @@ -1161,7 +1196,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test // var role = new IdentityRole("deleteNonEmpty"); // Assert.False(await roleMgr.RoleExistsAsync(role.Name)); // IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); - // var user = new User("t"); + // var user = new IdentityUser("t"); // IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); // IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); // Assert.Equal(1, role.Users.Count); @@ -1199,12 +1234,12 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test } } - private List GenerateUsers(string userNamePrefix, int count) + private List GenerateUsers(string userNamePrefix, int count) { - var users = new List(count); + var users = new List(count); for (var i=0; i( async () => await manager.AddToRoleAsync(u, "bogus")); @@ -1327,7 +1362,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var userMgr = CreateManager(context); var roleMgr = CreateRoleManager(context); var role = new IdentityRole("AddUserToRoleFailsIfAlreadyInRole"); - var user = new User("AddUserToRoleFailsIfAlreadyInRoleUser"); + var user = new IdentityUser("AddUserToRoleFailsIfAlreadyInRoleUser"); IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); @@ -1358,7 +1393,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); const string userName = "SetPhoneNumberTest"; - var user = new User(userName) { PhoneNumber = "123-456-7890" }; + var user = new IdentityUser(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"); @@ -1373,7 +1408,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); const string userName = "CanChangePhoneNumber"; - var user = new User(userName) { PhoneNumber = "123-456-7890" }; + var user = new IdentityUser(userName) { PhoneNumber = "123-456-7890" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); var stamp = await manager.GetSecurityStampAsync(user); @@ -1389,7 +1424,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); const string userName = "ChangePhoneNumberFailsWithWrongToken"; - var user = new User(userName) { PhoneNumber = "123-456-7890" }; + var user = new IdentityUser(userName) { PhoneNumber = "123-456-7890" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); var stamp = await manager.GetSecurityStampAsync(user); @@ -1405,7 +1440,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test { var manager = CreateManager(); const string userName = "CanVerifyPhoneNumber"; - var user = new User(userName); + var user = new IdentityUser(userName); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); const string num1 = "111-123-4567"; const string num2 = "111-111-1111"; @@ -1419,25 +1454,25 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test } #endif - private class EmailTokenProvider : IUserTokenProvider + private class EmailTokenProvider : IUserTokenProvider { - public Task GenerateAsync(string purpose, UserManager manager, User user, CancellationToken token) + public Task GenerateAsync(string purpose, UserManager manager, IdentityUser user, CancellationToken token) { return Task.FromResult(MakeToken(purpose)); } - public Task ValidateAsync(string purpose, string token, UserManager manager, - User user, CancellationToken cancellationToken) + public Task ValidateAsync(string purpose, string token, UserManager manager, + IdentityUser user, CancellationToken cancellationToken) { return Task.FromResult(token == MakeToken(purpose)); } - public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + public Task NotifyAsync(string token, UserManager manager, IdentityUser user, CancellationToken cancellationToken) { return manager.SendEmailAsync(user, token, token); } - public async Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + public async Task IsValidProviderForUserAsync(UserManager manager, IdentityUser user, CancellationToken token) { return !string.IsNullOrEmpty(await manager.GetEmailAsync(user)); } @@ -1448,25 +1483,25 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test } } - private class SmsTokenProvider : IUserTokenProvider + private class SmsTokenProvider : IUserTokenProvider { - public Task GenerateAsync(string purpose, UserManager manager, User user, CancellationToken token) + public Task GenerateAsync(string purpose, UserManager manager, IdentityUser user, CancellationToken token) { return Task.FromResult(MakeToken(purpose)); } - public Task ValidateAsync(string purpose, string token, UserManager manager, - User user, CancellationToken cancellationToken) + public Task ValidateAsync(string purpose, string token, UserManager manager, + IdentityUser user, CancellationToken cancellationToken) { return Task.FromResult(token == MakeToken(purpose)); } - public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + public Task NotifyAsync(string token, UserManager manager, IdentityUser user, CancellationToken cancellationToken) { return manager.SendSmsAsync(user, token); } - public async Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + public async Task IsValidProviderForUserAsync(UserManager manager, IdentityUser user, CancellationToken token) { return !string.IsNullOrEmpty(await manager.GetPhoneNumberAsync(user)); } @@ -1485,7 +1520,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test manager.EmailService = messageService; const string factorId = "EmailCode"; manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); - var user = new User("CanEmailTwoFactorToken") { Email = "foo@foo.com" }; + var user = new IdentityUser("CanEmailTwoFactorToken") { Email = "foo@foo.com" }; const string password = "password"; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); var stamp = user.SecurityStamp; @@ -1504,7 +1539,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task NotifyWithUnknownProviderFails() { var manager = CreateManager(); - var user = new User("NotifyFail"); + var user = new IdentityUser("NotifyFail"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); await ExceptionAssert.ThrowsAsync( @@ -1520,12 +1555,12 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test // var messageService = new TestMessageService(); // manager.EmailService = messageService; // const string factorId = "EmailCode"; - // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider // { // Subject = "Security Code", // BodyFormat = "Your code is: {0}" // }); - // var user = new User("EmailCodeTest") { Email = "foo@foo.com" }; + // var user = new IdentityUser("EmailCodeTest") { Email = "foo@foo.com" }; // const string password = "password"; // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); // var stamp = user.SecurityStamp; @@ -1545,8 +1580,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test //{ // var manager = CreateManager(); // const string factorId = "EmailCode"; - // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); - // var user = new User("EmailCodeTest") { Email = "foo@foo.com" }; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + // var user = new IdentityUser("EmailCodeTest") { Email = "foo@foo.com" }; // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); // var stamp = user.SecurityStamp; // Assert.NotNull(stamp); @@ -1560,7 +1595,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task EnableTwoFactorChangesSecurityStamp() { var manager = CreateManager(); - var user = new User("EnableTwoFactorChangesSecurityStamp"); + var user = new IdentityUser("EnableTwoFactorChangesSecurityStamp"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var stamp = user.SecurityStamp; Assert.NotNull(stamp); @@ -1575,7 +1610,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); var messageService = new TestMessageService(); manager.SmsService = messageService; - var user = new User("SmsTest") { PhoneNumber = "4251234567" }; + var user = new IdentityUser("SmsTest") { PhoneNumber = "4251234567" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); await manager.SendSmsAsync(user, "Hi"); Assert.NotNull(messageService.Message); @@ -1588,7 +1623,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); var messageService = new TestMessageService(); manager.EmailService = messageService; - var user = new User("CanSendEmail") { Email = "foo@foo.com" }; + var user = new IdentityUser("CanSendEmail") { Email = "foo@foo.com" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); await manager.SendEmailAsync(user, "Hi", "Body"); Assert.NotNull(messageService.Message); @@ -1604,7 +1639,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test manager.SmsService = messageService; const string factorId = "PhoneCode"; manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); - var user = new User("CanSmsTwoFactorToken") { PhoneNumber = "4251234567" }; + var user = new IdentityUser("CanSmsTwoFactorToken") { PhoneNumber = "4251234567" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var stamp = user.SecurityStamp; Assert.NotNull(stamp); @@ -1624,11 +1659,11 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test // var messageService = new TestMessageService(); // manager.SmsService = messageService; // const string factorId = "PhoneCode"; - // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider // { // MessageFormat = "Your code is: {0}" // }); - // var user = new User("PhoneCodeTest") { PhoneNumber = "4251234567" }; + // var user = new IdentityUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); // var stamp = user.SecurityStamp; // Assert.NotNull(stamp); @@ -1645,7 +1680,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() { var manager = CreateManager(); - var user = new User("GenerateTwoFactorWithUnknownFactorProviderWillThrow"); + var user = new IdentityUser("GenerateTwoFactorWithUnknownFactorProviderWillThrow"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); const string error = "No IUserTwoFactorProvider for 'bogus' is registered."; await @@ -1659,7 +1694,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test public async Task GetValidTwoFactorTestEmptyWithNoProviders() { var manager = CreateManager(); - var user = new User("GetValidTwoFactorTestEmptyWithNoProviders"); + var user = new IdentityUser("GetValidTwoFactorTestEmptyWithNoProviders"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var factors = await manager.GetValidTwoFactorProvidersAsync(user); Assert.NotNull(factors); @@ -1672,7 +1707,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider()); manager.RegisterTwoFactorProvider("email", new EmailTokenProvider()); - var user = new User("CanGetValidTwoFactor"); + var user = new IdentityUser("CanGetValidTwoFactor"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var factors = await manager.GetValidTwoFactorProvidersAsync(user); Assert.NotNull(factors); @@ -1698,8 +1733,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test //{ // var manager = CreateManager(); // var factorId = "PhoneCode"; - // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); - // var user = new User("PhoneCodeTest"); + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); + // var user = new IdentityUser("PhoneCodeTest"); // user.PhoneNumber = "4251234567"; // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); // var stamp = user.SecurityStamp; @@ -1716,7 +1751,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider()); manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider()); - var user = new User("WrongTokenProviderTest") { PhoneNumber = "4251234567" }; + var user = new IdentityUser("WrongTokenProviderTest") { PhoneNumber = "4251234567" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode"); Assert.NotNull(token); @@ -1729,7 +1764,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test var manager = CreateManager(); const string factorId = "PhoneCode"; manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); - var user = new User("VerifyWithWrongSmsTokenFails") { PhoneNumber = "4251234567" }; + var user = new IdentityUser("VerifyWithWrongSmsTokenFails") { PhoneNumber = "4251234567" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus")); } diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTestBase.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTestBase.cs new file mode 100644 index 0000000000..173010894a --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTestBase.cs @@ -0,0 +1,1677 @@ +// 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.Builder; +using Microsoft.AspNet.Identity.Test; +using Microsoft.AspNet.Testing; +using Microsoft.Data.Entity; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Microsoft.Framework.OptionsModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + public abstract class UserStoreTestBase + where ApplicationUser : IdentityUser, new() + where ApplicationRole : IdentityRole, new() + where TKey : IEquatable + { + public abstract string ConnectionString { get; } + + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(IServiceProvider services, IOptionsAccessor options) : base(services, options.Options) { } + } + + public ApplicationDbContext CreateContext(bool delete = false) + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddSqlServer(); + services.Add(OptionsServices.GetDefaultServices()); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + var serviceProvider = services.BuildServiceProvider(); + var db = new ApplicationDbContext(serviceProvider, + serviceProvider.GetService>()); + if (delete) + { + db.Database.EnsureDeleted(); + } + db.Database.EnsureCreated(); + return db; + } + + public void EnsureDatabase() + { + CreateContext(); + } + + public abstract UserManager CreateManager(ApplicationDbContext context); + + public UserManager CreateManager() + { + return CreateManager(CreateContext()); + } + + public abstract RoleManager CreateRoleManager(ApplicationDbContext context); + + public RoleManager CreateRoleManager() + { + return CreateRoleManager(CreateContext()); + } + + [TestPriority(-1)] + [Fact] + public void RecreateDatabase() + { + CreateContext(true); + } + + [Fact] + public async Task EnsureStartupUsageWorks() + { + EnsureDatabase(); + IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); + + builder.UseServices(services => + { + services.AddEntityFramework().AddSqlServer(); + services.AddIdentitySqlServer(); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + }); + + var userStore = builder.ApplicationServices.GetService>(); + var userManager = builder.ApplicationServices.GetService>(); + + Assert.NotNull(userStore); + Assert.NotNull(userManager); + + const string userName = "admin"; + const string password = "1qaz@WSX"; + var user = new ApplicationUser { UserName = userName }; + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); + IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); + } + + [Fact] + public async Task EnsureStartupOptionsChangeWorks() + { + EnsureDatabase(); + IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); + + builder.UseServices(services => + { + services.AddEntityFramework().AddSqlServer(); + services.AddIdentitySqlServer().SetupOptions(options => + { + options.Password.RequiredLength = 1; + options.Password.RequireLowercase = false; + options.Password.RequireNonLetterOrDigit = false; + options.Password.RequireUppercase = false; + options.Password.RequireDigit = false; + }); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + }); + + var userStore = builder.ApplicationServices.GetService>(); + var userManager = builder.ApplicationServices.GetService>(); + + Assert.NotNull(userStore); + Assert.NotNull(userManager); + + const string userName = "admin"; + const string password = "a"; + var user = new ApplicationUser { UserName = userName }; + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); + IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); + } + + [Fact] + public void CanCreateUserUsingEF() + { + using (var db = CreateContext()) + { + var guid = Guid.NewGuid().ToString(); + db.Users.Add(new ApplicationUser { UserName = guid }); + db.SaveChanges(); + Assert.True(db.Users.Any(u => u.UserName == guid)); + Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid)); + } + } + + [Fact] + public async Task CanCreateUsingManager() + { + var manager = CreateManager(); + var guid = Guid.NewGuid().ToString(); + var user = new ApplicationUser { UserName = "New" + guid }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanDeleteUser() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + Assert.Null(await manager.FindByIdAsync(user.Id.ToString())); + } + + [Fact] + public async Task CanUpdateUserName() + { + var manager = CreateManager(); + var oldName = Guid.NewGuid().ToString(); + var user = new ApplicationUser { UserName = oldName }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.Equal(user, await manager.FindByNameAsync(newName)); + Assert.Null(await manager.FindByNameAsync(oldName)); + //IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanUpdatePasswordUsingHasher() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "password")); + user.PasswordHash = manager.PasswordHasher.HashPassword("New"); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.False(await manager.CheckPasswordAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "New")); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanSetUserName() + { + var manager = CreateManager(); + var oldName = "CanSet" + Guid.NewGuid().ToString(); + var user = new ApplicationUser { UserName = oldName }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var newName = "PostSet" + 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)); + } + + //[Fact] + //public async Task RemoveClaimOnlyForUser() + //{ + // var manager = CreateManager(); + // var user = new ApplicationUser(); + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + //} + + [Fact] + public async Task UserValidatorCanBlockCreate() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task UserValidatorCanBlockUpdate() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) + { + var manager = CreateManager(); + var user = new ApplicationUser { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty."); + } + +#if NET45 + [Theory] + [InlineData("@@afd")] + [InlineData("bogus")] + public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) + { + var manager = CreateManager(); + var user = new ApplicationUser { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid."); + } +#endif + + [Fact] + public async Task PasswordValidatorCanBlockAddPassword() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task PasswordValidatorCanBlockChangePassword() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task CanCreateUserNoPassword() + { + var manager = CreateManager(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new ApplicationUser { UserName = "CanCreateUserNoPassword" })); + var user = await manager.FindByNameAsync("CanCreateUserNoPassword"); + Assert.NotNull(user); + Assert.Null(user.PasswordHash); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + } + + [Fact] + public async Task CanCreateUserAddLogin() + { + var manager = CreateManager(); + const string userName = "CanCreateUserAddLogin"; + const string provider = "ZzAuth"; + const string providerKey = "HaoKey"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new ApplicationUser { UserName = userName })); + var user = await manager.FindByNameAsync(userName); + Assert.NotNull(user); + var login = new UserLoginInfo(provider, providerKey); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(provider, logins.First().LoginProvider); + Assert.Equal(providerKey, logins.First().ProviderKey); + } + + [Fact] + public async Task CanCreateUserLoginAndAddPassword() + { + var manager = CreateManager(); + var login = new UserLoginInfo("Provider", "key"); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.False(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password")); + Assert.True(await manager.HasPasswordAsync(user)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + // REVIEW: need to remove this + manager = CreateManager(); + var fetch = await manager.FindByLoginAsync(login); + Assert.Equal(user.Id, fetch.Id); + fetch = await manager.FindByUserNamePasswordAsync(user.UserName, "password"); + Assert.Equal(user.Id, fetch.Id); + } + + [Fact] + public async Task AddPasswordFailsIfAlreadyHave() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); + Assert.True(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + "User already has a password set."); + } + + [Fact] + public async Task CanCreateUserAddRemoveLogin() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + var login = new UserLoginInfo("Provider", "key"); + var result = await manager.CreateAsync(user); + Assert.NotNull(user); + IdentityResultAssert.IsSuccess(result); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var fetch = await manager.FindByLoginAsync(login); + Assert.Equal(user.Id, fetch.Id); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(login.LoginProvider, logins.Last().LoginProvider); + Assert.Equal(login.ProviderKey, logins.Last().ProviderKey); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login)); + Assert.Null(await manager.FindByLoginAsync(login)); + logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanRemovePassword() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user)); + manager = CreateManager(); + var u = await manager.FindByNameAsync(user.UserName); + Assert.NotNull(u); + Assert.Null(u.PasswordHash); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanChangePassword() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword)); + Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + var fetch = await manager.FindByUserNamePasswordAsync(user.UserName, newPassword); + Assert.NotNull(fetch); + Assert.Equal(user.Id, fetch.Id); + Assert.Equal(user.UserName, fetch.UserName); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanAddRemoveUserClaim() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (var c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + Assert.NotNull(user.Claims.Single(cl => cl.ClaimType == c.Type && cl.ClaimValue == c.Value)); + } + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(3, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(2, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(0, userClaims.Count); + } + + [Fact] + public async Task ChangePasswordFallsIfPasswordWrong() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword"); + IdentityResultAssert.IsFailure(result, "Incorrect password."); + } + + [Fact] + public async Task AddDupeUserNameFails() + { + var manager = CreateManager(); + var user = new ApplicationUser { UserName = "AddDupeUserNameFails" }; + var user2 = new ApplicationUser { UserName = "AddDupeUserNameFails" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Name AddDupeUserNameFails is already taken."); + } + + [Fact] + public async Task AddDupeEmailAllowedByDefault() + { + var manager = CreateManager(); + var user = new ApplicationUser { Email = "yup@yup.com" }; + var user2 = new ApplicationUser { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + } + + [Fact] + public async Task AddDupeEmailFallsWhenUniqueEmailRequired() + { + var manager = CreateManager(); + manager.Options.User.RequireUniqueEmail = true; + var user = new ApplicationUser { Email = "dupeEmailTrue@yup.com" }; + var user2 = new ApplicationUser { Email = "dupeEmailTrue@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Email 'dupeEmailTrue@yup.com' is already taken."); + } + + [Fact] + public async Task UpdateSecurityStampActuallyChanges() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + Assert.Null(user.SecurityStamp); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task AddDupeLoginFails() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + var login = new UserLoginInfo("provder", "key"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var result = await manager.AddLoginAsync(user, login); + IdentityResultAssert.IsFailure(result, "A user with that external login already exists."); + } + + // Email tests + [Fact] + public async Task CanFindByEmail() + { + var manager = CreateManager(); + const string email = "email@test.com"; + var user = new ApplicationUser { Email = email }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var fetch = await manager.FindByEmailAsync(email); + //Assert.Equal(user, fetch); + Assert.Equal(user.Id, fetch.Id); + } + + [Fact] + public async Task CanFindUsersViaUserQuerable() + { + var mgr = CreateManager(); + var users = GenerateUsers("CanFindUsersViaUserQuerable", 3); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u)); + } + var usersQ = mgr.Users.Where(u => u.UserName.StartsWith("CanFindUsersViaUserQuerable")); + Assert.Null(mgr.Users.FirstOrDefault(u => u.UserName == "bogus")); + } + + [Fact] + public async Task EnsureRoleClaimNavigationProperty() + { + var context = CreateContext(); + var roleManager = CreateRoleManager(context); + var r = new ApplicationRole { Name = "EnsureRoleClaimNavigationProperty" }; + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + var c = new Claim("a", "b"); + IdentityResultAssert.IsSuccess(await roleManager.AddClaimAsync(r, c)); + Assert.NotNull(r.Claims.Single(cl => cl.ClaimValue == c.Value && cl.ClaimType == c.Type)); + } + + [Fact] + public async Task ClaimsIdentityCreatesExpectedClaims() + { + var context = CreateContext(); + var manager = CreateManager(context); + var role = CreateRoleManager(context); + var user = new ApplicationUser { UserName = "ClaimsIdentityCreatesExpectedClaims" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var admin = new ApplicationRole { Name = "Admin" }; + var local = new ApplicationRole { Name = "Local" }; + IdentityResultAssert.IsSuccess(await role.CreateAsync(admin)); + IdentityResultAssert.IsSuccess(await role.CreateAsync(local)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Admin")); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Local")); + Claim[] userClaims = + { + new Claim("Whatever", "Value"), + new Claim("Whatever2", "Value2") + }; + foreach (var c in userClaims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + } + Claim[] adminClaims = + { + new Claim("Admin", "Value"), + }; + foreach (var c in adminClaims) + { + IdentityResultAssert.IsSuccess(await role.AddClaimAsync(admin, c)); + } + Claim[] localClaims = + { + new Claim("Local", "Value"), + new Claim("Local2", "Value2") + }; + foreach (var c in localClaims) + { + IdentityResultAssert.IsSuccess(await role.AddClaimAsync(local, c)); + } + + var claimsFactory = new ClaimsIdentityFactory(manager, role); + var identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); + var claims = identity.Claims.ToList(); + Assert.NotNull(claims); + Assert.True( + claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id.ToString())); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local")); + foreach (var cl in userClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + foreach (var cl in adminClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + foreach (var cl in localClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + + // Remove a role claim and make sure its not there + IdentityResultAssert.IsSuccess(await role.RemoveClaimAsync(local, localClaims[0])); + identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); + claims = identity.Claims.ToList(); + Assert.False(claims.Any(c => c.Type == localClaims[0].Type && c.Value == localClaims[0].Value)); + Assert.True(claims.Any(c => c.Type == localClaims[1].Type && c.Value == localClaims[1].Value)); + } + + [Fact] + public async Task ConfirmEmailFalseByDefaultTest() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: No token provider implementations yet + private class StaticTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, + ApplicationUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose, user)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + ApplicationUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose, user)); + } + + public Task NotifyAsync(string token, UserManager manager, ApplicationUser user, CancellationToken cancellationToken) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUserAsync(UserManager manager, ApplicationUser user, CancellationToken token) + { + return Task.FromResult(true); + } + + private static string MakeToken(string purpose, ApplicationUser user) + { + return string.Join(":", user.Id, purpose, "ImmaToken"); + } + } + + [Fact] + public async Task CanResetPasswordWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + 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); + } + + [Fact] + public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword), + AlwaysBadValidator.ErrorMessage); + var fetch = await manager.FindByUserNamePasswordAsync(user.UserName, password); + Assert.Equal(user, fetch); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token."); + var fetch = await manager.FindByUserNamePasswordAsync(user.UserName, password); + Assert.Equal(user, fetch); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + var user2 = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + var token = await manager.GenerateUserTokenAsync("test", user); + Assert.True(await manager.VerifyUserTokenAsync(user, "test", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test2", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test", token + "a")); + Assert.False(await manager.VerifyUserTokenAsync(user2, "test", token)); + } + + [Fact] + public async Task CanConfirmEmailWithStaticToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + [Fact] + public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new ApplicationUser(); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: Can't reenable til we have a SecurityStamp linked token provider + //[Fact] + //public async Task ConfirmTokenFailsAfterPasswordChange() + //{ + // var manager = CreateManager(); + // var user = new ApplicationUser("test"); + // Assert.False(user.EmailConfirmed); + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + // var token = await manager.GenerateEmailConfirmationTokenAsync(user); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword")); + // IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token."); + // Assert.False(await manager.IsEmailConfirmedAsync(user)); + //} + + // Lockout tests + + [Fact] + public async Task SingleFailureLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 0; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task TwoFailureLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task ResetAccessCountPreventsLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task CanEnableLockoutManuallyAndLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset())); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); + } + + [Fact] + public async Task LockoutFailsIfNotEnabled() + { + var mgr = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()), + "Lockout is not enabled for this user."); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new ApplicationUser { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1))); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new ApplicationUser { LockoutEnd = DateTime.UtcNow.AddMinutes(5) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.True(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime()); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd)); + Assert.True(await mgr.IsLockedOutAsync(user)); + var end = await mgr.GetLockoutEndDateAsync(user); + Assert.Equal(lockoutEnd, end); + } + + // Role Tests + [Fact] + public async Task CanCreateRoleTest() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole { Name = "CanCreateRoleTest" }; + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + } + + private class AlwaysBadValidator : IUserValidator, IRoleValidator, + IPasswordValidator + { + public const string ErrorMessage = "I'm Bad."; + + public Task ValidateAsync(string password, UserManager manager, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(RoleManager manager, ApplicationRole role, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(UserManager manager, ApplicationUser user, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + [Fact] + public async Task BadValidatorBlocksCreateRole() + { + var manager = CreateRoleManager(); + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(new ApplicationRole()), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task BadValidatorBlocksRoleUpdate() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + var error = AlwaysBadValidator.ErrorMessage; + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(role), error); + } + + [Fact] + public async Task CanDeleteRole() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole(); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(role)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + } + + [Fact] + public async Task CanRoleFindById() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole(); + Assert.Null(await manager.FindByIdAsync(role.Id.ToString())); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + //Assert.Equal(role, await manager.FindByIdAsync(role.Id.ToString())); + var fetch = await manager.FindByIdAsync(role.Id.ToString()); + Assert.Equal(role.Id, fetch.Id); + Assert.Equal(role.Name, fetch.Name); + } + + [Fact] + public async Task CanRoleFindByName() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole(); + Assert.Null(await manager.FindByNameAsync(role.Name)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + var fetch = await manager.FindByNameAsync(role.Name); + //Assert.Equal(role, fetch); + Assert.Equal(role.Id, fetch.Id); + Assert.Equal(role.Name, fetch.Name); + } + + [Fact] + public async Task CanUpdateRoleName() + { + var manager = CreateRoleManager(); + var oldName = "OldRoleName" + Guid.NewGuid().ToString(); + var role = new ApplicationRole(); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + role.Name = "UpdatedRoleName" + Guid.NewGuid().ToString(); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role)); + Assert.False(await manager.RoleExistsAsync(oldName)); + var fetch = await manager.FindByNameAsync(role.Name); + Assert.Equal(role, fetch); + } + + [Fact] + public async Task CanQuerableRolesTest() + { + var manager = CreateRoleManager(); + var roles = GenerateRoles("CanQuerableRolesTest", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(r)); + } + Assert.Equal(roles.Count, manager.Roles.Count(r => r.Name.StartsWith("CanQuerableRolesTest"))); + var r1 = manager.Roles.FirstOrDefault(r => r.Name == "CanQuerableRolesTest1"); + Assert.Equal(roles[1], r1); + } + + [Fact] + public async Task DeleteRoleNonEmptySucceedsTest() + { + // Need fail if not empty? + var userMgr = CreateManager(); + var roleMgr = CreateRoleManager(); + var role = new ApplicationRole(); + Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + var roles = await userMgr.GetRolesAsync(user); + Assert.Equal(1, roles.Count()); + IdentityResultAssert.IsSuccess(await roleMgr.DeleteAsync(role)); + Assert.Null(await roleMgr.FindByNameAsync(role.Name)); + Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // REVIEW: We should throw if deleteing a non empty role? + roles = await userMgr.GetRolesAsync(user); + + Assert.Equal(0, roles.Count()); + } + + // TODO: cascading deletes? navigation properties not working + //[Fact] + //public async Task DeleteUserRemovesFromRoleTest() + //{ + // // Need fail if not empty? + // var userMgr = CreateManager(); + // var roleMgr = CreateRoleManager(); + // var role = new ApplicationRole("deleteNonEmpty"); + // Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + // var user = new ApplicationUser("t"); + // IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + // IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + // Assert.Equal(1, role.Users.Count); + // IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user)); + // role = await roleMgr.FindByIdAsync(role.Id); + // Assert.Equal(0, role.Users.Count); + //} + + [Fact] + public async Task CreateRoleFailsIfExists() + { + var manager = CreateRoleManager(); + var role = new ApplicationRole { Name = "CreateRoleFailsIfExists" }; + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + var role2 = new ApplicationRole { Name = "CreateRoleFailsIfExists" }; + IdentityResultAssert.IsFailure(await manager.CreateAsync(role2)); + } + + [Fact] + public async Task CanAddUsersToRole() + { + var context = CreateContext(); + var manager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var role = new ApplicationRole(); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role)); + var users = GenerateUsers("CanAddUsersToRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(u, role.Name)); + Assert.True(await manager.IsInRoleAsync(u, role.Name)); + } + } + + private List GenerateUsers(string userNamePrefix, int count) + { + var users = new List(count); + for (var i = 0; i < count; i++) + { + users.Add(new ApplicationUser { UserName = userNamePrefix + i }); + } + return users; + } + + private List GenerateRoles(string namePrefix, int count) + { + var roles = new List(count); + for (var i = 0; i < count; i++) + { + roles.Add(new ApplicationRole { Name = namePrefix + i }); + } + return roles; + } + + [Fact] + public async Task CanGetRolesForUser() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanGetRolesForUser", 4); + var roles = GenerateRoles("CanGetRolesForUserRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + foreach (var u in users) + { + var rs = await userManager.GetRolesAsync(u); + Assert.Equal(roles.Count, rs.Count); + foreach (var r in roles) + { + Assert.True(rs.Any(role => role == r.Name)); + } + } + } + + [Fact] + public async Task RemoveUserFromRoleWithMultipleRoles() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user)); + var roles = GenerateRoles("RemoveUserFromRoleWithMultipleRoles", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, r.Name)); + Assert.True(await userManager.IsInRoleAsync(user, r.Name)); + } + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(user, roles[2].Name)); + Assert.False(await userManager.IsInRoleAsync(user, roles[2].Name)); + } + + [Fact] + public async Task CanRemoveUsersFromRole() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanRemoveUsersFromRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + var r = new ApplicationRole(); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(u, r.Name)); + Assert.False(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + [Fact] + public async Task RemoveUserNotInRoleFails() + { + var context = CreateContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var role = new ApplicationRole(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var result = await userMgr.RemoveFromRoleAsync(user, role.Name); + IdentityResultAssert.IsFailure(result, "User is not in role."); + } + + [Fact] + public async Task AddUserToUnknownRoleFails() + { + var manager = CreateManager(); + var u = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + await Assert.ThrowsAsync( + async () => await manager.AddToRoleAsync(u, "bogus")); + } + + [Fact] + public async Task AddUserToRoleFailsIfAlreadyInRole() + { + var context = CreateContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var role = new ApplicationRole(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + Assert.True(await userMgr.IsInRoleAsync(user, role.Name)); + IdentityResultAssert.IsFailure(await userMgr.AddToRoleAsync(user, role.Name), "User already in role."); + } + + [Fact] + public async Task CanFindRoleByNameWithManager() + { + var roleMgr = CreateRoleManager(); + var role = new ApplicationRole(); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role.Id, (await roleMgr.FindByNameAsync(role.Name)).Id); + } + + [Fact] + public async Task CanFindRoleWithManager() + { + var roleMgr = CreateRoleManager(); + var role = new ApplicationRole(); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role, await roleMgr.FindByIdAsync(role.Id.ToString())); + } + + [Fact] + public async Task SetPhoneNumberTest() + { + var manager = CreateManager(); + var user = new ApplicationUser { 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"); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + +#if NET45 + [Fact] + public async Task CanChangePhoneNumber() + { + var manager = CreateManager(); + var user = new ApplicationUser { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1)); + Assert.True(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ChangePhoneNumberFailsWithWrongToken() + { + var manager = CreateManager(); + var user = new ApplicationUser { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"), + "Invalid token."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanVerifyPhoneNumber() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string num1 = "111-123-4567"; + const string num2 = "111-111-1111"; + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1); + var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2); + Assert.NotEqual(token1, token2); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1)); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2)); + } +#endif + + private class EmailTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, ApplicationUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + ApplicationUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, ApplicationUser user, CancellationToken cancellationToken) + { + return manager.SendEmailAsync(user, token, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, ApplicationUser user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetEmailAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "email:" + purpose; + } + } + + private class SmsTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, ApplicationUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + ApplicationUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, ApplicationUser user, CancellationToken cancellationToken) + { + return manager.SendSmsAsync(user, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, ApplicationUser user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetPhoneNumberAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "sms:" + purpose; + } + } + + [Fact] + public async Task CanEmailTwoFactorToken() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + const string factorId = "EmailCode"; + manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + var user = new ApplicationUser { Email = "foo@foo.com" }; + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Subject); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + [Fact] + public async Task NotifyWithUnknownProviderFails() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await + ExceptionAssert.ThrowsAsync( + async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"), + "No IUserTwoFactorProvider for 'Bogus' is registered."); + } + + + //[Fact] + //public async Task EmailTokenFactorWithFormatTest() + //{ + // var manager = CreateManager(); + // var messageService = new TestMessageService(); + // manager.EmailService = messageService; + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider + // { + // Subject = "Security Code", + // BodyFormat = "Your code is: {0}" + // }); + // var user = new ApplicationUser("EmailCodeTest") { Email = "foo@foo.com" }; + // const string password = "password"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Security Code", messageService.Message.Subject); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + //[Fact] + //public async Task EmailFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = CreateManager(); + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + // var user = new ApplicationUser("EmailCodeTest") { Email = "foo@foo.com" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task EnableTwoFactorChangesSecurityStamp() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + Assert.True(await manager.GetTwoFactorEnabledAsync(user)); + } + + [Fact] + public async Task CanSendSms() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + var user = new ApplicationUser { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendSmsAsync(user, "Hi"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Body); + } + + [Fact] + public async Task CanSendEmail() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + var user = new ApplicationUser { Email = "foo@foo.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendEmailAsync(user, "Hi", "Body"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Subject); + Assert.Equal("Body", messageService.Message.Body); + } + + [Fact] + public async Task CanSmsTwoFactorToken() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new ApplicationUser { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + //[Fact] + //public async Task PhoneTokenFactorFormatTest() + //{ + // var manager = CreateManager(); + // var messageService = new TestMessageService(); + // manager.SmsService = messageService; + // const string factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider + // { + // MessageFormat = "Your code is: {0}" + // }); + // var user = new ApplicationUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string error = "No IUserTwoFactorProvider for 'bogus' is registered."; + await + ExceptionAssert.ThrowsAsync( + () => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error); + await ExceptionAssert.ThrowsAsync( + () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"), error); + } + + [Fact] + public async Task GetValidTwoFactorTestEmptyWithNoProviders() + { + var manager = CreateManager(); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + } + + [Fact] + public async Task CanGetValidTwoFactor() + { + var manager = CreateManager(); + manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("email", new EmailTokenProvider()); + var user = new ApplicationUser(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 2); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + } + + //[Fact] + //public async Task PhoneFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = CreateManager(); + // var factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); + // var user = new ApplicationUser("PhoneCodeTest"); + // user.PhoneNumber = "4251234567"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task VerifyTokenFromWrongTokenProviderFails() + { + var manager = CreateManager(); + manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider()); + var user = new ApplicationUser { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode"); + Assert.NotNull(token); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "EmailCode", token)); + } + + [Fact] + public async Task VerifyWithWrongSmsTokenFails() + { + var manager = CreateManager(); + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new ApplicationUser { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus")); + } + + public class TestMessageService : IIdentityMessageService + { + public IdentityMessage Message { get; set; } + + public Task SendAsync(IdentityMessage message, CancellationToken token) + { + Message = message; + return Task.FromResult(0); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/HttpSignInTest.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/HttpSignInTest.cs index 082d91f270..2cb08b5a56 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/HttpSignInTest.cs +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/HttpSignInTest.cs @@ -40,10 +40,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test app.UseServices(services => { services.AddInstance(contextAccessor.Object); - services.AddIdentity(s => - { - s.AddInMemory(); - }).AddHttpSignIn(); + services.AddIdentity().AddInMemory().AddHttpSignIn(); }); // Act diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/StartupTest.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/StartupTest.cs index 0b34db44e4..62a32f956f 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/StartupTest.cs +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/StartupTest.cs @@ -21,10 +21,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test { var builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); - builder.UseServices(services => services.AddIdentity(s => - { - s.AddInMemory(); - })); + builder.UseServices(services => services.AddIdentity().AddInMemory()); var userStore = builder.ApplicationServices.GetService>(); var roleStore = builder.ApplicationServices.GetService>(); @@ -43,11 +40,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test public void VerifyUseInMemoryLifetimes() { var builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); - builder.UseServices(services => - { - services.AddIdentity(s => s.AddInMemory()); - - }); + builder.UseServices(services => services.AddIdentity().AddInMemory()); var userStore = builder.ApplicationServices.GetService>(); var roleStore = builder.ApplicationServices.GetService>(); diff --git a/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs b/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs index 9e14660fa3..5b23b31f8f 100644 --- a/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs @@ -20,8 +20,8 @@ namespace Microsoft.AspNet.Identity.Test var roleManager = MockHelpers.MockRoleManager().Object; var factory = new ClaimsIdentityFactory(userManager, roleManager); await Assert.ThrowsAsync("user", - async () => await factory.CreateAsync(null, "whatever")); - await Assert.ThrowsAsync("value", + async () => await factory.CreateAsync(null, new ClaimsIdentityOptions())); + await Assert.ThrowsAsync("options", async () => await factory.CreateAsync(new TestUser(), null)); } @@ -69,23 +69,22 @@ namespace Microsoft.AspNet.Identity.Test roleManager.Setup(m => m.GetClaimsAsync(local, CancellationToken.None)).ReturnsAsync(localClaims); } - const string authType = "Microsoft.AspNet.Identity"; var factory = new ClaimsIdentityFactory(userManager.Object, roleManager.Object); // Act - var identity = await factory.CreateAsync(user, authType); + var identity = await factory.CreateAsync(user, new ClaimsIdentityOptions()); // Assert var manager = userManager.Object; Assert.NotNull(identity); - Assert.Equal(authType, identity.AuthenticationType); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); var claims = identity.Claims.ToList(); Assert.NotNull(claims); Assert.True( - 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")); + claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id)); + Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin")); + Assert.Equal(supportRoles, claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local")); foreach (var cl in userClaims) { Assert.Equal(supportClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); diff --git a/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs b/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs index 8eb3374c99..0ff25f845a 100644 --- a/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Identity.Test { var services = new ServiceCollection(); var validator = new UserValidator(); - services.AddIdentity(b => b.AddUserValidator(() => validator)); + services.AddIdentity().AddUserValidator(() => validator); Assert.Equal(validator, services.BuildServiceProvider().GetService>()); } @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Identity.Test { var services = new ServiceCollection(); var validator = new PasswordValidator(); - services.AddIdentity(b => b.AddPasswordValidator(() => validator)); + services.AddIdentity().AddPasswordValidator(() => validator); Assert.Equal(validator, services.BuildServiceProvider().GetService>()); } @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Identity.Test public void EnsureDefaultServices() { var services = new ServiceCollection(); - services.AddIdentity(identity => { }); + services.AddIdentity(); var provider = services.BuildServiceProvider(); var userValidator = provider.GetService>() as UserValidator; @@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Identity.Test private static void CanOverride(TService instance) { var services = new ServiceCollection(); - services.AddIdentity(b => b.AddInstance(() => instance)); + services.AddIdentity().AddInstance(() => instance); Assert.Equal(instance, services.BuildServiceProvider().GetService()); } diff --git a/test/Microsoft.AspNet.Identity.Test/IdentityOptionsTest.cs b/test/Microsoft.AspNet.Identity.Test/IdentityOptionsTest.cs index 1b773c31c6..094807548c 100644 --- a/test/Microsoft.AspNet.Identity.Test/IdentityOptionsTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/IdentityOptionsTest.cs @@ -32,10 +32,10 @@ namespace Microsoft.AspNet.Identity.Test 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); + Assert.Equal(ClaimTypes.Role, options.ClaimsIdentity.RoleClaimType); + Assert.Equal(ClaimTypes.Name, options.ClaimsIdentity.UserNameClaimType); + Assert.Equal(ClaimTypes.NameIdentifier, options.ClaimsIdentity.UserIdClaimType); + Assert.Equal(ClaimsIdentityOptions.DefaultSecurityStampClaimType, options.ClaimsIdentity.SecurityStampClaimType); } [Fact] @@ -45,13 +45,15 @@ namespace Microsoft.AspNet.Identity.Test const string usernameClaimType = "namez"; const string useridClaimType = "idz"; const string securityStampClaimType = "stampz"; + const string authType = "auth"; var dic = new Dictionary - { - {"identity:claimtype:role", roleClaimType}, - {"identity:claimtype:username", usernameClaimType}, - {"identity:claimtype:userid", useridClaimType}, - {"identity:claimtype:securitystamp", securityStampClaimType}, + { + {"identity:claimsidentity:authENTICATIONType", authType}, + {"identity:claimsidentity:roleclaimtype", roleClaimType}, + {"identity:claimsidentity:usernameclaimtype", usernameClaimType}, + {"identity:claimsidentity:useridclaimtype", useridClaimType}, + {"identity:claimsidentity:securitystampclaimtype", securityStampClaimType}, {"identity:user:requireUniqueEmail", "true"}, {"identity:password:RequiredLength", "10"}, {"identity:password:RequireNonLetterOrDigit", "false"}, @@ -62,17 +64,18 @@ namespace Microsoft.AspNet.Identity.Test {"identity:lockout:MaxFailedAccessAttempts", "1000"} }; var config = new Configuration { new MemoryConfigurationSource(dic) }; - Assert.Equal(roleClaimType, config.Get("identity:claimtype:role")); + Assert.Equal(roleClaimType, config.Get("identity:claimsidentity:roleclaimtype")); var services = new ServiceCollection {OptionsServices.GetDefaultServices()}; services.AddIdentity(config.GetSubKey("identity")); var accessor = services.BuildServiceProvider().GetService>(); Assert.NotNull(accessor); var options = accessor.Options; - 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.Equal(authType, options.ClaimsIdentity.AuthenticationType); + Assert.Equal(roleClaimType, options.ClaimsIdentity.RoleClaimType); + Assert.Equal(useridClaimType, options.ClaimsIdentity.UserIdClaimType); + Assert.Equal(usernameClaimType, options.ClaimsIdentity.UserNameClaimType); + Assert.Equal(securityStampClaimType, options.ClaimsIdentity.SecurityStampClaimType); Assert.True(options.User.RequireUniqueEmail); Assert.True(options.User.AllowOnlyAlphanumericNames); Assert.True(options.User.AllowOnlyAlphanumericNames); @@ -124,7 +127,7 @@ namespace Microsoft.AspNet.Identity.Test var app = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); app.UseServices(services => { - services.AddIdentity(identityServices => identityServices.SetupOptions(options => options.User.RequireUniqueEmail = true)); + services.AddIdentity().SetupOptions(options => options.User.RequireUniqueEmail = true); }); var optionsGetter = app.ApplicationServices.GetService>(); diff --git a/test/Microsoft.AspNet.Identity.Test/PasswordValidatorTest.cs b/test/Microsoft.AspNet.Identity.Test/PasswordValidatorTest.cs index e1e9336892..c373eb9eb8 100644 --- a/test/Microsoft.AspNet.Identity.Test/PasswordValidatorTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/PasswordValidatorTest.cs @@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Identity.Test 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."); + "Passwords must have at least one non letter and non digit character."); } [Theory] @@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Identity.Test [InlineData("aB1@df", Errors.None)] public async Task UberMixedRequiredTests(string input, Errors errorMask) { - const string alphaError = "Passwords must have at least one non letter or digit character."; + const string alphaError = "Passwords must have at least one non letter and non digit character."; const string upperError = "Passwords must have at least one uppercase ('A'-'Z')."; const string lowerError = "Passwords must have at least one lowercase ('a'-'z')."; const string digitError = "Passwords must have at least one digit ('0'-'9')."; diff --git a/test/Shared/MockHelpers.cs b/test/Shared/MockHelpers.cs index 81d24bb51e..e9736036ac 100644 --- a/test/Shared/MockHelpers.cs +++ b/test/Shared/MockHelpers.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Identity.Test { var services = new ServiceCollection(); services.Add(OptionsServices.GetDefaultServices()); - services.AddIdentity(b => b.AddUserStore(storeFunc)); + services.AddIdentity().AddUserStore(storeFunc); services.SetupOptions(options => { options.Password.RequireDigit = false; diff --git a/test/Shared/UserManagerTestBase.cs b/test/Shared/UserManagerTestBase.cs index 80eed4d87f..e9aeb84340 100644 --- a/test/Shared/UserManagerTestBase.cs +++ b/test/Shared/UserManagerTestBase.cs @@ -410,14 +410,15 @@ namespace Microsoft.AspNet.Identity.Test } var claimsFactory = new ClaimsIdentityFactory(manager, role); - var identity = await claimsFactory.CreateAsync(user, "test"); + var identity = await claimsFactory.CreateAsync(user, new ClaimsIdentityOptions()); var claims = identity.Claims.ToList(); Assert.NotNull(claims); + Assert.Equal(DefaultAuthenticationTypes.ApplicationCookie, identity.AuthenticationType); Assert.True( - 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.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")); + claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserNameClaimType && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.UserIdClaimType && c.Value == user.Id)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimsIdentity.RoleClaimType && c.Value == "Local")); foreach (var cl in userClaims) { Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value));