[Fixes #1639] Reset password doesn't work

This commit is contained in:
Javier Calvarro Nelson 2018-02-17 21:49:30 -08:00
parent 2511862a77
commit 3e30d52055
9 changed files with 171 additions and 7 deletions

View File

@ -36,7 +36,7 @@
</div>
<div class="form-group">
<p>
<a asp-page="./ForgotPassword">Forgot your password?</a>
<a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a>
</p>
<p>
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>

View File

@ -31,12 +31,13 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
public string Code { get; set; }
public virtual IActionResult OnGet(string code = null) => throw new NotImplementedException();
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
public virtual IActionResult OnGet(string code = null) => throw new NotImplementedException();
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class ResetPasswordModel<TUser> : ResetPasswordModel where TUser : IdentityUser
@ -48,7 +49,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
_userManager = userManager;
}
public IActionResult OnGet(string code = null)
public override IActionResult OnGet(string code = null)
{
if (code == null)
{
@ -64,7 +65,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
}
}
public async Task<IActionResult> OnPostAsync()
public override async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{

View File

@ -145,5 +145,32 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
await UserStories.RegisterNewUserWithSocialLoginAsync(client, userName, email);
await UserStories.LoginWithSocialLoginAsync(newClient, userName);
}
[Fact]
public async Task CanLogInAfterResettingThePassword()
{
// Arrange
var emailSender = new ContosoEmailSender();
var server = ServerFactory.CreateServer(b => b.ConfigureServices(s =>
s.SetupTestEmailSender(emailSender)));
var client = ServerFactory.CreateDefaultClient(server);
var resetPasswordClient = ServerFactory.CreateDefaultClient(server);
var newClient = ServerFactory.CreateDefaultClient(server);
var userName = $"{Guid.NewGuid()}@example.com";
var password = $"!Test.Password1$";
var newPassword = $"!New.Password1$";
await UserStories.RegisterNewUserAsync(client, userName, password);
var registrationEmail = Assert.Single(emailSender.SentEmails);
await UserStories.ConfirmEmailAsync(registrationEmail, client);
// Act & Assert
await UserStories.ForgotPasswordAsync(resetPasswordClient, userName);
Assert.Equal(2, emailSender.SentEmails.Count);
var email = emailSender.SentEmails[1];
await UserStories.ResetPasswordAsync(resetPasswordClient, email, userName, newPassword);
await UserStories.LoginExistingUserAsync(newClient, userName, newPassword);
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
{
public class ForgotPassword : DefaultUIPage
{
private readonly IHtmlFormElement _forgotPasswordForm;
public ForgotPassword(HttpClient client, IHtmlDocument document, DefaultUIContext context) : base(client, document, context)
{
_forgotPasswordForm = HtmlAssert.HasForm(document);
}
public async Task<ForgotPasswordConfirmation> SendForgotPasswordAsync(string email)
{
var response = await Client.SendAsync(_forgotPasswordForm, new Dictionary<string, string>
{
["Input_Email"] = email
});
var goToForgotPasswordConfirmation = ResponseAssert.IsRedirect(response);
var forgotPasswordConfirmationResponse = await Client.GetAsync(goToForgotPasswordConfirmation);
var forgotPasswordConfirmation = await ResponseAssert.IsHtmlDocumentAsync(forgotPasswordConfirmationResponse);
return new ForgotPasswordConfirmation(Client, forgotPasswordConfirmation, Context);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
{
public class ForgotPasswordConfirmation : DefaultUIPage
{
public ForgotPasswordConfirmation(HttpClient client, IHtmlDocument document, DefaultUIContext context) : base(client, document, context)
{
}
}
}

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
using Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account;
using Xunit;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
@ -12,6 +13,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
public class Login : DefaultUIPage
{
private readonly IHtmlFormElement _loginForm;
private readonly IHtmlAnchorElement _forgotPasswordLink;
private readonly IHtmlFormElement _externalLoginForm;
private readonly IHtmlElement _contosoButton;
@ -22,6 +24,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
: base(client, login, context)
{
_loginForm = HtmlAssert.HasForm("#account", login);
_forgotPasswordLink = HtmlAssert.HasLink("#forgot-password", login);
if (Context.ContosoLoginEnabled)
{
_externalLoginForm = HtmlAssert.HasForm("#external-account", login);
@ -40,6 +43,14 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
return new Contoso.Login(Client, contosoLogin, Context);
}
public async Task<ForgotPassword> ClickForgotPasswordLinkAsync()
{
var response = await Client.GetAsync(_forgotPasswordLink.Href);
var forgotPassword = await ResponseAssert.IsHtmlDocumentAsync(response);
return new ForgotPassword(Client, forgotPassword, Context);
}
public async Task<Index> LoginValidUserAsync(string userName, string password)
{
var loggedIn = await SendLoginForm(userName, password);

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account
{
public class ResetPassword : DefaultUIPage
{
private readonly IHtmlFormElement _resetPasswordForm;
public ResetPassword(HttpClient client, IHtmlDocument resetPassword, DefaultUIContext context) : base(client, resetPassword, context)
{
_resetPasswordForm = HtmlAssert.HasForm(resetPassword);
}
internal static async Task<ResetPassword> CreateAsync(IHtmlAnchorElement link, HttpClient client, DefaultUIContext context)
{
var resetPasswordResponse = await client.GetAsync(link.Href);
var resetPassword = await ResponseAssert.IsHtmlDocumentAsync(resetPasswordResponse);
return new ResetPassword(client, resetPassword, context);
}
public async Task<ResetPasswordConfirmation> SendNewPasswordAsync(string email, string newPassword)
{
var resetPasswordResponse = await Client.SendAsync(_resetPasswordForm, new Dictionary<string, string>
{
["Input_Email"] = email,
["Input_Password"] = newPassword,
["Input_ConfirmPassword"] = newPassword
});
var goToResetPasswordConfirmation = ResponseAssert.IsRedirect(resetPasswordResponse);
var resetPasswordConfirmationResponse = await Client.GetAsync(goToResetPasswordConfirmation);
var resetPasswordConfirmation = await ResponseAssert.IsHtmlDocumentAsync(resetPasswordConfirmationResponse);
return new ResetPasswordConfirmation(Client, resetPasswordConfirmation, Context);
}
}
}

View File

@ -0,0 +1,13 @@
using System.Net.Http;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account
{
public class ResetPasswordConfirmation : DefaultUIPage
{
public ResetPasswordConfirmation(HttpClient client, IHtmlDocument resetPasswordConfirmation, DefaultUIContext context)
: base(client, resetPasswordConfirmation, context)
{
}
}
}

View File

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
using Identity.DefaultUI.WebSite.Services;
using Microsoft.AspNetCore.Identity.FunctionalTests.Account;
using Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage;
using Microsoft.AspNetCore.Identity.FunctionalTests.Pages.Account;
using Xunit;
@ -36,7 +37,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
internal static async Task<Index> RegisterNewUserWithSocialLoginAsync(HttpClient client, string userName, string email)
{
var index = await Index.CreateAsync(client,new DefaultUIContext().WithSocialLoginEnabled());
var index = await Index.CreateAsync(client, new DefaultUIContext().WithSocialLoginEnabled());
var login = await index.ClickLoginLinkAsync();
@ -114,5 +115,26 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
.WithExistingUser()
.WithConfirmedEmail());
}
internal static async Task<ForgotPasswordConfirmation> ForgotPasswordAsync(HttpClient client, string userName)
{
var index = await Index.CreateAsync(client);
var login = await index.ClickLoginLinkAsync();
var forgotPassword = await login.ClickForgotPasswordLinkAsync();
return await forgotPassword.SendForgotPasswordAsync(userName);
}
internal static async Task<ResetPasswordConfirmation> ResetPasswordAsync(HttpClient client, IdentityEmail resetPasswordEmail, string email, string newPassword)
{
var emailBody = HtmlAssert.IsHtmlFragment(resetPasswordEmail.Body);
var linkElement = HtmlAssert.HasElement("a", emailBody);
var link = Assert.IsAssignableFrom<IHtmlAnchorElement>(linkElement);
var resetPassword = await ResetPassword.CreateAsync(link, client, new DefaultUIContext().WithExistingUser());
return await resetPassword.SendNewPasswordAsync(email, newPassword);
}
}
}