diff --git a/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs index e80f317c2f..1d613f55d2 100644 --- a/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs +++ b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs @@ -15,7 +15,9 @@ namespace Microsoft.AspNet.Identity.InMemory IUserEmailStore, IUserLockoutStore, IUserPhoneNumberStore, - IQueryableUserStore where TUser : InMemoryUser + IQueryableUserStore, + IUserTwoFactorStore + where TUser : InMemoryUser { private readonly Dictionary _logins = new Dictionary(new LoginComparer()); @@ -267,5 +269,16 @@ namespace Microsoft.AspNet.Identity.InMemory return (obj.ProviderKey + "--" + obj.LoginProvider).GetHashCode(); } } + + public Task SetTwoFactorEnabled(TUser user, bool enabled) + { + user.TwoFactorEnabled = enabled; + return Task.FromResult(0); + } + + public Task GetTwoFactorEnabled(TUser user) + { + return Task.FromResult(user.TwoFactorEnabled); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IRoleValidator.cs b/src/Microsoft.AspNet.Identity/IRoleValidator.cs index 400182e995..2d72a2994d 100644 --- a/src/Microsoft.AspNet.Identity/IRoleValidator.cs +++ b/src/Microsoft.AspNet.Identity/IRoleValidator.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; namespace Microsoft.AspNet.Identity @@ -5,14 +6,18 @@ namespace Microsoft.AspNet.Identity /// /// Used to validate a role /// - /// - public interface IRoleValidator + /// + /// + public interface IRoleValidator + where TRole : class, IRole + where TKey : IEquatable { /// /// Validate the user /// /// + /// /// - Task Validate(T role); + Task Validate(RoleManager manager, TRole role); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityResult.cs b/src/Microsoft.AspNet.Identity/IdentityResult.cs index 3ff9bb4a1b..f45d2e2234 100644 --- a/src/Microsoft.AspNet.Identity/IdentityResult.cs +++ b/src/Microsoft.AspNet.Identity/IdentityResult.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace Microsoft.AspNet.Identity { @@ -23,9 +24,9 @@ namespace Microsoft.AspNet.Identity /// public IdentityResult(IEnumerable errors) { - if (errors == null) + if (errors == null || !errors.Any()) { - errors = new[] {"Resources.DefaultError"}; + errors = new[] {Resources.DefaultError}; } Succeeded = false; Errors = errors; diff --git a/src/Microsoft.AspNet.Identity/RoleManager.cs b/src/Microsoft.AspNet.Identity/RoleManager.cs index feea399ca5..4feac38ad2 100644 --- a/src/Microsoft.AspNet.Identity/RoleManager.cs +++ b/src/Microsoft.AspNet.Identity/RoleManager.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Identity throw new ArgumentNullException("store"); } Store = store; - RoleValidator = new RoleValidator(this); + RoleValidator = new RoleValidator(); } /// @@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Identity /// /// Used to validate roles before persisting changes /// - public IRoleValidator RoleValidator { get; set; } + public IRoleValidator RoleValidator { get; set; } /// /// Returns an IQueryable of roles if the store is an IQueryableRoleStore @@ -71,6 +71,18 @@ namespace Microsoft.AspNet.Identity } } + /// + /// Returns true if the store is an IQueryableRoleStore + /// + public virtual bool SupportsQueryableRoles + { + get + { + ThrowIfDisposed(); + return Store is IQueryableRoleStore; + } + } + /// /// Dispose this object /// @@ -82,7 +94,7 @@ namespace Microsoft.AspNet.Identity private async Task ValidateRoleInternal(TRole role) { - return (RoleValidator == null) ? IdentityResult.Success : await RoleValidator.Validate(role).ConfigureAwait(false); + return (RoleValidator == null) ? IdentityResult.Success : await RoleValidator.Validate(this, role).ConfigureAwait(false); } /// diff --git a/src/Microsoft.AspNet.Identity/RoleValidator.cs b/src/Microsoft.AspNet.Identity/RoleValidator.cs index 3d3bd3a877..1024021047 100644 --- a/src/Microsoft.AspNet.Identity/RoleValidator.cs +++ b/src/Microsoft.AspNet.Identity/RoleValidator.cs @@ -10,38 +10,28 @@ namespace Microsoft.AspNet.Identity /// /// /// - public class RoleValidator : IRoleValidator + public class RoleValidator : IRoleValidator where TRole : class, IRole where TKey : IEquatable { /// - /// Constructor + /// Validates a role before saving /// /// - public RoleValidator(RoleManager manager) + /// + /// + public virtual async Task Validate(RoleManager manager, TRole role) { if (manager == null) { throw new ArgumentNullException("manager"); } - Manager = manager; - } - - private RoleManager Manager { get; set; } - - /// - /// Validates a role before saving - /// - /// - /// - public virtual async Task Validate(TRole item) - { - if (item == null) + if (role == null) { - throw new ArgumentNullException("item"); + throw new ArgumentNullException("role"); } var errors = new List(); - await ValidateRoleName(item, errors); + await ValidateRoleName(manager, role, errors); if (errors.Count > 0) { return IdentityResult.Failed(errors.ToArray()); @@ -49,7 +39,7 @@ namespace Microsoft.AspNet.Identity return IdentityResult.Success; } - private async Task ValidateRoleName(TRole role, List errors) + private static async Task ValidateRoleName(RoleManager manager, TRole role, ICollection errors) { if (string.IsNullOrWhiteSpace(role.Name)) { @@ -57,7 +47,7 @@ namespace Microsoft.AspNet.Identity } else { - var owner = await Manager.FindByName(role.Name); + var owner = await manager.FindByName(role.Name); if (owner != null && !EqualityComparer.Default.Equals(owner.Id, role.Id)) { errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, role.Name)); diff --git a/src/Microsoft.AspNet.Identity/UserManager.cs b/src/Microsoft.AspNet.Identity/UserManager.cs index 9ddb24914d..1d77de8716 100644 --- a/src/Microsoft.AspNet.Identity/UserManager.cs +++ b/src/Microsoft.AspNet.Identity/UserManager.cs @@ -271,7 +271,6 @@ namespace Microsoft.AspNet.Identity } } - /// /// Returns an IQueryable of users if the store is an IQueryableUserStore /// @@ -1162,14 +1161,14 @@ namespace Microsoft.AspNet.Identity throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound, userId)); } - if (await VerifyChangePhoneNumberToken(userId, token, phoneNumber).ConfigureAwait(false)) + if (!await VerifyChangePhoneNumberToken(userId, token, phoneNumber).ConfigureAwait(false)) { - await store.SetPhoneNumber(user, phoneNumber).ConfigureAwait(false); - await store.SetPhoneNumberConfirmed(user, true).ConfigureAwait(false); - await UpdateSecurityStampInternal(user).ConfigureAwait(false); - return await Update(user).ConfigureAwait(false); + return IdentityResult.Failed(Resources.InvalidToken); } - return IdentityResult.Failed(Resources.InvalidToken); + await store.SetPhoneNumber(user, phoneNumber).ConfigureAwait(false); + await store.SetPhoneNumberConfirmed(user, true).ConfigureAwait(false); + await UpdateSecurityStampInternal(user).ConfigureAwait(false); + return await Update(user).ConfigureAwait(false); } /// @@ -1520,12 +1519,12 @@ namespace Microsoft.AspNet.Identity throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound, userId)); } - if (await store.GetLockoutEnabled(user).ConfigureAwait(false)) + if (!await store.GetLockoutEnabled(user).ConfigureAwait(false)) { - var lockoutTime = await store.GetLockoutEndDate(user).ConfigureAwait((false)); - return lockoutTime >= DateTimeOffset.UtcNow; + return false; } - return false; + var lockoutTime = await store.GetLockoutEndDate(user).ConfigureAwait((false)); + return lockoutTime >= DateTimeOffset.UtcNow; } /// @@ -1627,13 +1626,14 @@ namespace Microsoft.AspNet.Identity } // If this puts the user over the threshold for lockout, lock them out and reset the access failed count var count = await store.IncrementAccessFailedCount(user).ConfigureAwait(false); - if (count >= MaxFailedAccessAttemptsBeforeLockout) + if (count < MaxFailedAccessAttemptsBeforeLockout) { - await - store.SetLockoutEndDate(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan)) - .ConfigureAwait(false); - await store.ResetAccessFailedCount(user).ConfigureAwait(false); + return await Update(user).ConfigureAwait(false); } + await + store.SetLockoutEndDate(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan)) + .ConfigureAwait(false); + await store.ResetAccessFailedCount(user).ConfigureAwait(false); return await Update(user).ConfigureAwait(false); } diff --git a/src/Microsoft.AspNet.Identity/UserValidator.cs b/src/Microsoft.AspNet.Identity/UserValidator.cs index 06791e6a31..2f8d8652fd 100644 --- a/src/Microsoft.AspNet.Identity/UserValidator.cs +++ b/src/Microsoft.AspNet.Identity/UserValidator.cs @@ -94,20 +94,20 @@ namespace Microsoft.AspNet.Identity } /// - /// Returns true if the character is upper, lower, or a digit + /// Returns true if the character is upper, lower, a digit, or a common email character [@_.] /// /// /// public virtual bool IsAlphaNumeric(char c) { - return IsUpper(c) || IsLower(c) || IsDigit(c) || c == '@' || c == '_'; + return IsUpper(c) || IsLower(c) || IsDigit(c) || c == '@' || c == '_' || c == '.'; } private async Task ValidateUserName(UserManager manager, TUser user, ICollection errors) { if (string.IsNullOrWhiteSpace(user.UserName)) { - errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Name")); + errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "UserName")); } else if (AllowOnlyAlphanumericUserNames && !user.UserName.All(IsAlphaNumeric)) { diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/ExceptionHelper.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/ExceptionHelper.cs deleted file mode 100644 index d377797078..0000000000 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/ExceptionHelper.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNet.Identity.InMemory.Test -{ - public static class ExceptionHelper - { - public static async Task ThrowsWithError(Func act, string error) - where TException : Exception - { - var e = await Assert.ThrowsAsync(act); - if (e != null) - { - Assert.Equal(error, e.Message); - } - return e; - } - - public static async Task ThrowsArgumentException(Func del, string exceptionMessage, - string paramName) - { - var e = await Assert.ThrowsAsync(del); - // Only check exception message on English build and OS, since some exception messages come from the OS - // and will be in the native language. - // TODO: needed? if (IdentityResultAssert.EnglishBuildAndOS) - //{ - Assert.Equal(exceptionMessage, e.Message); - Assert.Equal(paramName, e.ParamName);; - //} - return e; - } - - public static Task ThrowsArgumentNullOrEmpty(Func del, string paramName) - { - return ThrowsArgumentException(del, "Value cannot be null or empty.\r\nParameter name: " + paramName, - paramName); - } - - public static async Task ThrowsArgumentNull(Func del, string paramName) - { - var e = await Assert.ThrowsAsync(del); - Assert.Equal(paramName, e.ParamName); - return e; - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStoreTest.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryStoreTest.cs similarity index 79% rename from test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStoreTest.cs rename to test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryStoreTest.cs index c851ed2be6..604a8e68b9 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStoreTest.cs +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryStoreTest.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Testing; using Xunit; namespace Microsoft.AspNet.Identity.InMemory.Test @@ -57,7 +58,9 @@ namespace Microsoft.AspNet.Identity.InMemory.Test var user = new InMemoryUser("CreateUserLoginAddPasswordTest"); IdentityResultAssert.IsSuccess(await manager.Create(user)); IdentityResultAssert.IsSuccess(await manager.AddLogin(user.Id, login)); + Assert.False(await manager.HasPassword(user.Id)); IdentityResultAssert.IsSuccess(await manager.AddPassword(user.Id, "password")); + Assert.True(await manager.HasPassword(user.Id)); var logins = await manager.GetLogins(user.Id); Assert.NotNull(logins); Assert.Equal(1, logins.Count()); @@ -165,6 +168,27 @@ namespace Microsoft.AspNet.Identity.InMemory.Test IdentityResultAssert.IsFailure(await manager.Create(user2), "Name dupe is already taken."); } + [Fact] + public async Task AddDupeEmailAllowedByDefault() + { + var manager = CreateManager(); + var user = new InMemoryUser("dupe") {Email = "yup@yup.com"}; + var user2 = new InMemoryUser("dupeEmail") { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.Create(user)); + IdentityResultAssert.IsSuccess(await manager.Create(user2)); + } + + [Fact] + public async Task AddDupeEmailFallsWhenUniqueEmailRequired() + { + var manager = CreateManager(); + manager.UserValidator = new UserValidator { RequireUniqueEmail = true }; + var user = new InMemoryUser("dupe") { Email = "yup@yup.com" }; + var user2 = new InMemoryUser("dupeEmail") { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.Create(user)); + IdentityResultAssert.IsFailure(await manager.Create(user2), "Email 'yup@yup.com' is already taken."); + } + [Fact] public async Task UpdateSecurityStampActuallyChanges() { @@ -235,23 +259,91 @@ namespace Microsoft.AspNet.Identity.InMemory.Test } // TODO: No token provider implementations yet - //[Fact] - //public async Task ConfirmEmailTest() - //{ - // var manager = CreateManager(); - // var user = new InMemoryUser("test"); - // Assert.False(user.EmailConfirmed); - // IdentityResultAssert.IsSuccess(await manager.Create(user)); - // var token = await manager.GenerateEmailConfirmationToken(user.Id); - // Assert.NotNull(token); - // IdentityResultAssert.IsSuccess(await manager.ConfirmEmail(user.Id, token)); - // Assert.True(await manager.IsEmailConfirmed(user.Id)); - // IdentityResultAssert.IsSuccess(await manager.SetEmail(user.Id, null)); - // Assert.False(await manager.IsEmailConfirmed(user.Id)); - //} + private class StaticTokenProvider : IUserTokenProvider + { + private static string MakeToken(string purpose, IUser user) + { + return string.Join(":", user.Id, purpose, "ImmaToken"); + } + public Task Generate(string purpose, UserManager manager, + InMemoryUser user) + { + return Task.FromResult(MakeToken(purpose, user)); + } + + public Task Validate(string purpose, string token, UserManager manager, + InMemoryUser user) + { + return Task.FromResult(token == MakeToken(purpose, user)); + } + + public Task Notify(string token, UserManager manager, InMemoryUser user) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUser(UserManager manager, InMemoryUser user) + { + return Task.FromResult(true); + } + } + + + [Fact] + public async Task CanResetPasswordWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("ResetPasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.Create(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetToken(user.Id); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ResetPassword(user.Id, token, newPassword)); + Assert.Null(await manager.Find(user.UserName, password)); + Assert.Equal(user, await manager.Find(user.UserName, newPassword)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("UserTokenTest"); + var user2 = new InMemoryUser("UserTokenTest2"); + IdentityResultAssert.IsSuccess(await manager.Create(user)); + IdentityResultAssert.IsSuccess(await manager.Create(user2)); + var token = await manager.GenerateUserToken("test", user.Id); + Assert.True(await manager.VerifyUserToken(user.Id, "test", token)); + Assert.False(await manager.VerifyUserToken(user.Id, "test2", token)); + Assert.False(await manager.VerifyUserToken(user.Id, "test", token + "a")); + Assert.False(await manager.VerifyUserToken(user2.Id, "test", token)); + } + + [Fact] + public async Task CanConfirmEmailWithStaticToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("test"); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.Create(user)); + var token = await manager.GenerateEmailConfirmationToken(user.Id); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ConfirmEmail(user.Id, token)); + Assert.True(await manager.IsEmailConfirmed(user.Id)); + IdentityResultAssert.IsSuccess(await manager.SetEmail(user.Id, null)); + Assert.False(await manager.IsEmailConfirmed(user.Id)); + } + + // TODO: Can't reenable til we have a SecurityStamp linked token provider //[Fact] - //public async Task ConfirmTokenFailsAfterPasswordChangeTest() + //public async Task ConfirmTokenFailsAfterPasswordChange() //{ // var manager = CreateManager(); // var user = new InMemoryUser("test"); @@ -446,25 +538,40 @@ namespace Microsoft.AspNet.Identity.InMemory.Test Assert.True(await manager.RoleExists(role.Name)); } - //[Fact] - //public async Task BadValidatorBlocksCreateTest() - //{ - // var manager = CreateRoleManager(); - // manager.RoleValidator = new AlwaysBadValidator(); - // IdentityResultAssert.IsFailure(await manager.Create(new InMemoryRole("blocked")), - // AlwaysBadValidator.ErrorMessage); - //} + private class AlwaysBadValidator : IUserValidator, IRoleValidator + { + public const string ErrorMessage = "I'm Bad."; - //[Fact] - //public async Task BadValidatorBlocksAllUpdatesTest() - //{ - // var manager = CreateRoleManager(); - // var role = new InMemoryRole("poorguy"); - // IdentityResultAssert.IsSuccess(await manager.Create(role)); - // var error = AlwaysBadValidator.ErrorMessage; - // manager.RoleValidator = new AlwaysBadValidator(); - // IdentityResultAssert.IsFailure(await manager.Update(role), error); - //} + public Task Validate(UserManager manager, InMemoryUser user) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task Validate(RoleManager manager, InMemoryRole role) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + [Fact] + public async Task BadValidatorBlocksCreateRole() + { + var manager = CreateRoleManager(); + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.Create(new InMemoryRole("blocked")), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task BadValidatorBlocksRoleUpdate() + { + var manager = CreateRoleManager(); + var role = new InMemoryRole("poorguy"); + IdentityResultAssert.IsSuccess(await manager.Create(role)); + var error = AlwaysBadValidator.ErrorMessage; + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.Update(role), error); + } [Fact] public async Task CanDeleteRoleTest() @@ -801,28 +908,76 @@ namespace Microsoft.AspNet.Identity.InMemory.Test Assert.False(await manager.VerifyChangePhoneNumberToken(user.Id, token1, num2)); } - //[Fact] - //public async Task EmailTokenFactorTest() - //{ - // var manager = CreateManager(); - // var messageService = new TestMessageService(); - // manager.EmailService = messageService; - // const string factorId = "EmailCode"; - // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); - // var user = new InMemoryUser("EmailCodeTest") { Email = "foo@foo.com" }; - // const string password = "password"; - // IdentityResultAssert.IsSuccess(await manager.Create(user, password)); - // var stamp = user.SecurityStamp; - // Assert.NotNull(stamp); - // var token = await manager.GenerateTwoFactorToken(user.Id, factorId); - // Assert.NotNull(token); - // Assert.Null(messageService.Message); - // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorToken(user.Id, factorId, token)); - // Assert.NotNull(messageService.Message); - // Assert.Equal(String.Empty, messageService.Message.Subject); - // Assert.Equal(token, messageService.Message.Body); - // Assert.True(await manager.VerifyTwoFactorToken(user.Id, factorId, token)); - //} + private class EmailTokenProvider : IUserTokenProvider + { + + public Task Generate(string purpose, UserManager manager, InMemoryUser user) + { + return Task.FromResult(purpose); + } + + public Task Validate(string purpose, string token, UserManager manager, InMemoryUser user) + { + return Task.FromResult(token == purpose); + } + + public Task Notify(string token, UserManager manager, InMemoryUser user) + { + return manager.SendEmail(user.Id, token, token); + } + + public Task IsValidProviderForUser(UserManager manager, InMemoryUser user) + { + return Task.FromResult(true); + } + } + + private class SmsTokenProvider : IUserTokenProvider + { + + public Task Generate(string purpose, UserManager manager, InMemoryUser user) + { + return Task.FromResult(purpose); + } + + public Task Validate(string purpose, string token, UserManager manager, InMemoryUser user) + { + return Task.FromResult(token == purpose); + } + + public Task Notify(string token, UserManager manager, InMemoryUser user) + { + return manager.SendSms(user.Id, token); + } + + public Task IsValidProviderForUser(UserManager manager, InMemoryUser user) + { + return Task.FromResult(true); + } + } + + [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 InMemoryUser("EmailCodeTest") { Email = "foo@foo.com" }; + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.Create(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorToken(user.Id, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorToken(user.Id, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Subject); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorToken(user.Id, factorId, token)); + } //[Fact] //public async Task EmailTokenFactorWithFormatTest() @@ -867,20 +1022,18 @@ namespace Microsoft.AspNet.Identity.InMemory.Test // Assert.False(await manager.VerifyTwoFactorToken(user.Id, factorId, token)); //} - //[Fact] - //public async Task UserTwoFactorProviderTest() - //{ - // var manager = CreateManager(); - // const string factorId = "PhoneCode"; - // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); - // var user = new InMemoryUser("PhoneCodeTest"); - // IdentityResultAssert.IsSuccess(await manager.Create(user)); - // var stamp = user.SecurityStamp; - // Assert.NotNull(stamp); - // IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabled(user.Id, true)); - // Assert.NotEqual(stamp, await manager.GetSecurityStamp(user.Id)); - // Assert.True(await manager.GetTwoFactorEnabled(user.Id)); - //} + [Fact] + public async Task EnableTwoFactorChangesSecurityStamp() + { + var manager = CreateManager(); + var user = new InMemoryUser("TwoFactorEnabledTest"); + IdentityResultAssert.IsSuccess(await manager.Create(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabled(user.Id, true)); + Assert.NotEqual(stamp, await manager.GetSecurityStamp(user.Id)); + Assert.True(await manager.GetTwoFactorEnabled(user.Id)); + } [Fact] public async Task CanSendSms() @@ -909,26 +1062,26 @@ namespace Microsoft.AspNet.Identity.InMemory.Test Assert.Equal("Body", messageService.Message.Body); } - //[Fact] - //public async Task PhoneTokenFactorTest() - //{ - // var manager = CreateManager(); - // var messageService = new TestMessageService(); - // manager.SmsService = messageService; - // const string factorId = "PhoneCode"; - // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); - // var user = new InMemoryUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; - // IdentityResultAssert.IsSuccess(await manager.Create(user)); - // var stamp = user.SecurityStamp; - // Assert.NotNull(stamp); - // var token = await manager.GenerateTwoFactorToken(user.Id, factorId); - // Assert.NotNull(token); - // Assert.Null(messageService.Message); - // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorToken(user.Id, factorId, token)); - // Assert.NotNull(messageService.Message); - // Assert.Equal(token, messageService.Message.Body); - // Assert.True(await manager.VerifyTwoFactorToken(user.Id, factorId, token)); - //} + [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 InMemoryUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.Create(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorToken(user.Id, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorToken(user.Id, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorToken(user.Id, factorId, token)); + } //[Fact] //public async Task PhoneTokenFactorFormatTest() @@ -961,8 +1114,8 @@ namespace Microsoft.AspNet.Identity.InMemory.Test var user = new InMemoryUser("PhoneCodeTest"); IdentityResultAssert.IsSuccess(await manager.Create(user)); const string error = "No IUserTwoFactorProvider for 'bogus' is registered."; - await ExceptionHelper.ThrowsWithError(() => manager.GenerateTwoFactorToken(user.Id, "bogus"), error); - await ExceptionHelper.ThrowsWithError( + await ExceptionAssert.ThrowsAsync(() => manager.GenerateTwoFactorToken(user.Id, "bogus"), error); + await ExceptionAssert.ThrowsAsync( () => manager.VerifyTwoFactorToken(user.Id, "bogus", "bogus"), error); } diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/project.json b/test/Microsoft.AspNet.Identity.InMemory.Test/project.json index 0b49648d13..b690e71e9a 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/project.json +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/project.json @@ -4,6 +4,7 @@ "Microsoft.AspNet.Identity" : "0.1-alpha-*", "Microsoft.AspNet.Identity.InMemory" : "0.1-alpha-*", "Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*", + "Microsoft.AspNet.Testing" : "0.1-alpha-*", "Xunit.KRunner": "0.1-alpha-*", "xunit.abstractions": "2.0.0-aspnet-*", "xunit.assert": "2.0.0-aspnet-*", diff --git a/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs b/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs new file mode 100644 index 0000000000..1c08139357 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.Test +{ + public class IdentityResultTest + { + [Fact] + public void VerifyDefaultConstructor() + { + var result = new IdentityResult(); + Assert.False(result.Succeeded); + Assert.Equal(1, result.Errors.Count()); + Assert.Equal("An unknown failure has occured.", result.Errors.First()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.Test/NoopRoleStore.cs b/test/Microsoft.AspNet.Identity.Test/NoopRoleStore.cs new file mode 100644 index 0000000000..8ee0303c78 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/NoopRoleStore.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.Test +{ + public class NoopRoleStore : IRoleStore + { + public Task Create(TestRole user) + { + return Task.FromResult(0); + } + + public Task Update(TestRole user) + { + return Task.FromResult(0); + } + + public Task FindById(string roleId) + { + return Task.FromResult(null); + } + + public Task FindByName(string userName) + { + return Task.FromResult(null); + } + + public void Dispose() + { + } + + public Task Delete(TestRole user) + { + return Task.FromResult(0); + } + } + +} diff --git a/test/Microsoft.AspNet.Identity.Test/NoopUserStore.cs b/test/Microsoft.AspNet.Identity.Test/NoopUserStore.cs new file mode 100644 index 0000000000..e8503276e5 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/NoopUserStore.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.Test +{ + public class NoopUserStore : IUserStore + { + public Task Create(TestUser user) + { + return Task.FromResult(0); + } + + public Task Update(TestUser user) + { + return Task.FromResult(0); + } + + public Task FindById(string userId) + { + return Task.FromResult(null); + } + + public Task FindByName(string userName) + { + return Task.FromResult(null); + } + + public void Dispose() + { + } + + public Task Delete(TestUser user) + { + return Task.FromResult(0); + } + } + +} diff --git a/test/Microsoft.AspNet.Identity.Test/RoleManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/RoleManagerTest.cs new file mode 100644 index 0000000000..11a814df34 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/RoleManagerTest.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Moq; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.Test +{ + public class RoleManagerTest + { + [Fact] + public void ConstructorThrowsWithNullStore() + { + Assert.Throws("store", () => new RoleManager(null)); + } + + [Fact] + public void RolesQueryableFailWhenStoreNotImplemented() + { + var manager = new RoleManager(new NoopRoleStore()); + Assert.False(manager.SupportsQueryableRoles); + Assert.Throws(() => manager.Roles.Count()); + } + + [Fact] + public void DisposeAfterDisposeDoesNotThrow() + { + var manager = new RoleManager(new NoopRoleStore()); + manager.Dispose(); + manager.Dispose(); + } + + [Fact] + public async Task RoleManagerPublicNullChecks() + { + Assert.Throws("store", + () => new RoleManager(null)); + var manager = new RoleManager(new NotImplementedStore()); + await Assert.ThrowsAsync("role", async () => await manager.Create(null)); + await Assert.ThrowsAsync("role", async () => await manager.Update(null)); + await Assert.ThrowsAsync("role", async () => await manager.Delete(null)); + await Assert.ThrowsAsync("roleName", async () => await manager.FindByName(null)); + } + + private class NotImplementedStore : IRoleStore + { + + public Task Create(TestRole role) + { + throw new NotImplementedException(); + } + + public Task Update(TestRole role) + { + throw new NotImplementedException(); + } + + public Task Delete(TestRole role) + { + throw new NotImplementedException(); + } + + public Task FindById(string roleId) + { + throw new NotImplementedException(); + } + + public Task FindByName(string roleName) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + throw new NotImplementedException(); + } + } + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.Test/TestRole.cs b/test/Microsoft.AspNet.Identity.Test/TestRole.cs new file mode 100644 index 0000000000..3cd26972e3 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/TestRole.cs @@ -0,0 +1,9 @@ + +namespace Microsoft.AspNet.Identity.Test +{ + public class TestRole : IRole + { + public string Id { get; private set; } + public string Name { get; set; } + } +} diff --git a/test/Microsoft.AspNet.Identity.Test/TestUser.cs b/test/Microsoft.AspNet.Identity.Test/TestUser.cs new file mode 100644 index 0000000000..c27bfabac8 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/TestUser.cs @@ -0,0 +1,9 @@ + +namespace Microsoft.AspNet.Identity.Test +{ + public class TestUser : IUser + { + public string Id { get; private set; } + public string UserName { get; set; } + } +} diff --git a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs index 0d0ef8e20f..506f68720a 100644 --- a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Security.Claims; +using Microsoft.AspNet.Testing; using Moq; using System; using System.Linq; @@ -143,122 +144,151 @@ namespace Microsoft.AspNet.Identity.Test } [Fact] - public void DisposeAfterDisposeWorksTest() + public void DisposeAfterDisposeDoesNotThrow() { var manager = new UserManager(new NoopUserStore()); manager.Dispose(); manager.Dispose(); } - [Fact] - public async Task ManagerPublicNullCheckTest() + private class BadPasswordValidtor : IPasswordValidator { - Assert.Throws(() => new UserManager((IUserStore)null)); + public const string ErrorMessage = "I'm Bad."; + + public Task Validate(string password) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + [Fact] + public async Task PasswordValidatorBlocksCreate() + { + // TODO: Can switch to Mock eventually + var manager = new UserManager(new EmptyStore()) + { + PasswordValidator = new BadPasswordValidtor() + }; + IdentityResultAssert.IsFailure(await manager.Create(new TestUser(), "password"), + BadPasswordValidtor.ErrorMessage); + } + + [Fact] + public async Task ManagerPublicNullChecks() + { + Assert.Throws("store", () => new UserManager((IUserStore)null)); var manager = new UserManager(new NotImplementedStore()); Assert.Throws(() => manager.ClaimsIdentityFactory = null); Assert.Throws(() => manager.PasswordHasher = null); - await Assert.ThrowsAsync(async () => await manager.CreateIdentity(null, "whatever")); - await Assert.ThrowsAsync(async () => await manager.Create(null)); - await Assert.ThrowsAsync(async () => await manager.Create(null, null)); - await Assert.ThrowsAsync(async () => await manager.Create(new TestUser(), null)); - await Assert.ThrowsAsync(async () => await manager.Update(null)); - await Assert.ThrowsAsync(async () => await manager.Delete(null)); - await Assert.ThrowsAsync(async () => await manager.AddClaim("bogus", null)); - await Assert.ThrowsAsync(async () => await manager.FindByName(null)); - await Assert.ThrowsAsync(async () => await manager.Find(null, null)); - await Assert.ThrowsAsync(async () => await manager.AddLogin("bogus", null)); - await Assert.ThrowsAsync(async () => await manager.RemoveLogin("bogus", null)); - await Assert.ThrowsAsync(async () => await manager.FindByEmail(null)); - Assert.Throws(() => manager.RegisterTwoFactorProvider(null, null)); - Assert.Throws(() => manager.RegisterTwoFactorProvider("bogus", null)); + await Assert.ThrowsAsync("user", async () => await manager.CreateIdentity(null, "whatever")); + await Assert.ThrowsAsync("user", async () => await manager.Create(null)); + await Assert.ThrowsAsync("user", async () => await manager.Create(null, null)); + await Assert.ThrowsAsync("password", async () => await manager.Create(new TestUser(), null)); + await Assert.ThrowsAsync("user", async () => await manager.Update(null)); + await Assert.ThrowsAsync("user", async () => await manager.Delete(null)); + await Assert.ThrowsAsync("claim", async () => await manager.AddClaim("bogus", null)); + await Assert.ThrowsAsync("userName", async () => await manager.FindByName(null)); + await Assert.ThrowsAsync("userName", async () => await manager.Find(null, null)); + await Assert.ThrowsAsync("login", async () => await manager.AddLogin("bogus", null)); + await Assert.ThrowsAsync("login", async () => await manager.RemoveLogin("bogus", null)); + await Assert.ThrowsAsync("email", async () => await manager.FindByEmail(null)); + Assert.Throws("twoFactorProvider", () => manager.RegisterTwoFactorProvider(null, null)); + Assert.Throws("provider", () => manager.RegisterTwoFactorProvider("bogus", null)); } - //[Fact] - //public void MethodsFailWithUnknownUserTest() - //{ - // var db = IdentityResultExtensions.CreateDefaultDb(); - // var manager = new UserManager(new UserStore(db)); - // manager.UserTokenProvider = new NoOpTokenProvider(); - // var error = "UserId not found."; - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.AddClaimAsync(null, new Claim("a", "b"))), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.AddLoginAsync(null, new UserLoginInfo("", ""))), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.AddPasswordAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.AddToRoleAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.ChangePasswordAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetClaimsAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetLoginsAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetRolesAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.IsInRoleAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.RemoveClaimAsync(null, new Claim("a", "b"))), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.RemoveLoginAsync(null, new UserLoginInfo("", ""))), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.RemovePasswordAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.RemoveFromRoleAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.UpdateSecurityStampAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetSecurityStampAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.HasPasswordAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GeneratePasswordResetTokenAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.ResetPasswordAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.IsEmailConfirmedAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GenerateEmailConfirmationTokenAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.ConfirmEmailAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetEmailAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.SetEmailAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.IsPhoneNumberConfirmedAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.ChangePhoneNumberAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.VerifyChangePhoneNumberTokenAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetPhoneNumberAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.SetPhoneNumberAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetTwoFactorEnabledAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.SetTwoFactorEnabledAsync(null, true)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GenerateTwoFactorTokenAsync(null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.VerifyTwoFactorTokenAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.NotifyTwoFactorTokenAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.GetValidTwoFactorProvidersAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.VerifyUserTokenAsync(null, null, null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.AccessFailedAsync(null)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.SetLockoutEnabledAsync(null, false)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.SetLockoutEndDateAsync(null, DateTimeOffset.UtcNow)), error); - // ExceptionHelper.ThrowsWithError( - // () => AsyncHelper.RunSync(() => manager.IsLockedOutAsync(null)), error); - //} + [Fact] + public async Task MethodsFailWithUnknownUserTest() + { + var manager = new UserManager(new EmptyStore()) + { + UserTokenProvider = new NoOpTokenProvider() + }; + const string error = "UserId not found."; + await ExceptionAssert.ThrowsAsync( + async () => await manager.AddClaim(null, new Claim("a", "b")), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.AddLogin(null, new UserLoginInfo("", "")), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.AddPassword(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.AddToRole(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.ChangePassword(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetClaims(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetLogins(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetRoles(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.IsInRole(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.RemoveClaim(null, new Claim("a", "b")), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.RemoveLogin(null, new UserLoginInfo("", "")), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.RemovePassword(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.RemoveFromRole(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.UpdateSecurityStamp(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetSecurityStamp(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.HasPassword(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GeneratePasswordResetToken(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.ResetPassword(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.IsEmailConfirmed(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GenerateEmailConfirmationToken(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.ConfirmEmail(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetEmail(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.SetEmail(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.IsPhoneNumberConfirmed(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.ChangePhoneNumber(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.VerifyChangePhoneNumberToken(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetPhoneNumber(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.SetPhoneNumber(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetTwoFactorEnabled(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.SetTwoFactorEnabled(null, true), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GenerateTwoFactorToken(null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.VerifyTwoFactorToken(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.NotifyTwoFactorToken(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetValidTwoFactorProviders(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.VerifyUserToken(null, null, null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.AccessFailed(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.ResetAccessFailedCount(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetLockoutEnabled(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.SetLockoutEnabled(null, false), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.SetLockoutEndDate(null, DateTimeOffset.UtcNow), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.GetLockoutEndDate(null), error); + await ExceptionAssert.ThrowsAsync( + async () => await manager.IsLockedOut(null), error); + } [Fact] public async Task MethodsThrowWhenDisposedTest() @@ -297,14 +327,45 @@ namespace Microsoft.AspNet.Identity.Test await Assert.ThrowsAsync(() => manager.ConfirmEmail(null, null)); } - private class TestUser : IUser + private class NoOpTokenProvider : IUserTokenProvider { - public string Id { get; private set; } - public string UserName { get; set; } + + public Task Generate(string purpose, UserManager manager, TestUser user) + { + return Task.FromResult("Test"); + } + + public Task Validate(string purpose, string token, UserManager manager, TestUser user) + { + return Task.FromResult(true); + } + + public Task Notify(string token, UserManager manager, TestUser user) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUser(UserManager manager, TestUser user) + { + return Task.FromResult(true); + } } - private class NoopUserStore : IUserStore + private class EmptyStore : + IUserPasswordStore, + IUserClaimStore, + IUserLoginStore, + IUserEmailStore, + IUserPhoneNumberStore, + IUserLockoutStore, + IUserTwoFactorStore, + IUserRoleStore, + IUserSecurityStampStore { + public void Dispose() + { + } + public Task Create(TestUser user) { return Task.FromResult(0); @@ -315,6 +376,11 @@ namespace Microsoft.AspNet.Identity.Test return Task.FromResult(0); } + public Task Delete(TestUser user) + { + return Task.FromResult(0); + } + public Task FindById(string userId) { return Task.FromResult(null); @@ -325,18 +391,177 @@ namespace Microsoft.AspNet.Identity.Test return Task.FromResult(null); } - public void Dispose() - { - } - - public Task Delete(TestUser user) + public Task SetPasswordHash(TestUser user, string passwordHash) { return Task.FromResult(0); } + + public Task GetPasswordHash(TestUser user) + { + return Task.FromResult(null); + } + + public Task HasPassword(TestUser user) + { + return Task.FromResult(false); + } + + public Task> GetClaims(TestUser user) + { + return Task.FromResult>(new List()); + } + + public Task AddClaim(TestUser user, Claim claim) + { + return Task.FromResult(0); + } + + public Task RemoveClaim(TestUser user, Claim claim) + { + return Task.FromResult(0); + } + + public Task AddLogin(TestUser user, UserLoginInfo login) + { + return Task.FromResult(0); + } + + public Task RemoveLogin(TestUser user, UserLoginInfo login) + { + return Task.FromResult(0); + } + + public Task> GetLogins(TestUser user) + { + return Task.FromResult>(new List()); + } + + public Task Find(UserLoginInfo login) + { + return Task.FromResult(null); + } + + public Task SetEmail(TestUser user, string email) + { + return Task.FromResult(0); + } + + public Task GetEmail(TestUser user) + { + return Task.FromResult(""); + } + + public Task GetEmailConfirmed(TestUser user) + { + return Task.FromResult(false); + } + + public Task SetEmailConfirmed(TestUser user, bool confirmed) + { + return Task.FromResult(0); + } + + public Task FindByEmail(string email) + { + return Task.FromResult(null); + } + + public Task SetPhoneNumber(TestUser user, string phoneNumber) + { + return Task.FromResult(0); + } + + public Task GetPhoneNumber(TestUser user) + { + return Task.FromResult(""); + } + + public Task GetPhoneNumberConfirmed(TestUser user) + { + return Task.FromResult(false); + } + + public Task SetPhoneNumberConfirmed(TestUser user, bool confirmed) + { + return Task.FromResult(0); + } + + public Task GetLockoutEndDate(TestUser user) + { + return Task.FromResult(DateTimeOffset.MinValue); + } + + public Task SetLockoutEndDate(TestUser user, DateTimeOffset lockoutEnd) + { + return Task.FromResult(0); + } + + public Task IncrementAccessFailedCount(TestUser user) + { + return Task.FromResult(0); + } + + public Task ResetAccessFailedCount(TestUser user) + { + return Task.FromResult(0); + } + + public Task GetAccessFailedCount(TestUser user) + { + return Task.FromResult(0); + } + + public Task GetLockoutEnabled(TestUser user) + { + return Task.FromResult(false); + } + + public Task SetLockoutEnabled(TestUser user, bool enabled) + { + return Task.FromResult(0); + } + + public Task SetTwoFactorEnabled(TestUser user, bool enabled) + { + return Task.FromResult(0); + } + + public Task GetTwoFactorEnabled(TestUser user) + { + return Task.FromResult(false); + } + + public Task AddToRole(TestUser user, string roleName) + { + return Task.FromResult(0); + } + + public Task RemoveFromRole(TestUser user, string roleName) + { + return Task.FromResult(0); + } + + public Task> GetRoles(TestUser user) + { + return Task.FromResult>(new List()); + } + + public Task IsInRole(TestUser user, string roleName) + { + return Task.FromResult(false); + } + + public Task SetSecurityStamp(TestUser user, string stamp) + { + return Task.FromResult(0); + } + + public Task GetSecurityStamp(TestUser user) + { + return Task.FromResult(""); + } } - - private class NotImplementedStore : IUserPasswordStore, IUserClaimStore, diff --git a/test/Microsoft.AspNet.Identity.Test/UserValidatorTest.cs b/test/Microsoft.AspNet.Identity.Test/UserValidatorTest.cs new file mode 100644 index 0000000000..f97444ffd9 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.Test/UserValidatorTest.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.Test +{ + public class UserValidatorTest + { + [Fact] + public async Task ValidateThrowsWithNull() + { + // Setup + var manager = new UserManager(new NoopUserStore()); + var validator = new UserValidator(); + + // Act + // Assert + await Assert.ThrowsAsync("manager", () => validator.Validate(null, null)); + await Assert.ThrowsAsync("user", () => validator.Validate(manager, null)); + } + + [Theory, + InlineData(null), + InlineData("")] + public async Task ValidateFailsWithTooShortUserNames(string input) + { + // Setup + var manager = new UserManager(new NoopUserStore()); + var validator = new UserValidator(); + var user = new TestUser() { UserName = input }; + + // Act + var result = await validator.Validate(manager, user); + + // Assert + IdentityResultAssert.IsFailure(result, "UserName cannot be null or empty."); + } + + [Theory, + InlineData("test_email@foo.com", true), + InlineData("hao", true), + InlineData("test123", true), + InlineData("!noway", false), + InlineData("foo@boz#.com", false), + ] + public async Task DefaultAlphaNumericOnlyUserNameValidation(string userName, bool expectSuccess) + { + // Setup + var manager = new UserManager(new NoopUserStore()); + var validator = new UserValidator(); + var user = new TestUser() {UserName=userName}; + + // Act + var result = await validator.Validate(manager, user); + + // Assert + if (expectSuccess) + { + IdentityResultAssert.IsSuccess(result); + } + else + { + IdentityResultAssert.IsFailure(result); + } + } + + [Theory, + InlineData("test_email@foo.com", true), + InlineData("hao", true), + InlineData("test123", true), + InlineData("!noway", true), + InlineData("foo@boz#.com", true), + ] + public async Task CanAllowNonAlphaNumericUserName(string userName, bool expectSuccess) + { + // Setup + var manager = new UserManager(new NoopUserStore()); + var validator = new UserValidator() { AllowOnlyAlphanumericUserNames = false }; + var user = new TestUser() { UserName = userName }; + + // Act + var result = await validator.Validate(manager, user); + + // Assert + if (expectSuccess) + { + IdentityResultAssert.IsSuccess(result); + } + else + { + IdentityResultAssert.IsFailure(result); + } + } + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.Test/project.json b/test/Microsoft.AspNet.Identity.Test/project.json index 704fa33343..5ef20b1348 100644 --- a/test/Microsoft.AspNet.Identity.Test/project.json +++ b/test/Microsoft.AspNet.Identity.Test/project.json @@ -1,8 +1,10 @@ { "version": "0.1-alpha-*", "dependencies": { - "Microsoft.AspNet.Identity" : "0.1-alpha-*", + "Microsoft.AspNet.ConfigurationModel" : "0.1-alpha-*", "Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*", + "Microsoft.AspNet.Identity" : "0.1-alpha-*", + "Microsoft.AspNet.Testing" : "0.1-alpha-*", "Xunit.KRunner": "0.1-alpha-*", "xunit.abstractions": "2.0.0-aspnet-*", "xunit.assert": "2.0.0-aspnet-*",