mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Improving lost password functionality
--HG-- branch : dev
This commit is contained in:
@@ -231,13 +231,14 @@ namespace Orchard.Tests.Modules.Users.Controllers {
|
||||
public void ResetPasswordLinkShouldBeSent() {
|
||||
var registrationSettings = _container.Resolve<IWorkContextAccessor>().GetContext().CurrentSite.As<RegistrationSettingsPart>();
|
||||
registrationSettings.UsersCanRegister = true;
|
||||
registrationSettings.EnableLostPassword = true;
|
||||
_session.Flush();
|
||||
|
||||
_controller.Register("bar", "bar@baz.com", "66554321", "66554321");
|
||||
_session.Flush();
|
||||
|
||||
_controller.Url = new UrlHelper(new RequestContext(new HttpContextStub(), new RouteData()));
|
||||
var result = _controller.LostPassword("bar");
|
||||
var result = _controller.RequestLostPassword("bar");
|
||||
Assert.That(result, Is.TypeOf<RedirectToRouteResult>());
|
||||
|
||||
Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("LogOn"));
|
||||
|
@@ -141,12 +141,23 @@ namespace Orchard.Users.Controllers {
|
||||
return Register();
|
||||
}
|
||||
|
||||
public ActionResult LostPassword() {
|
||||
public ActionResult RequestLostPassword() {
|
||||
// ensure users can request lost password
|
||||
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
|
||||
if ( !registrationSettings.EnableLostPassword ) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult LostPassword(string username) {
|
||||
public ActionResult RequestLostPassword(string username) {
|
||||
// ensure users can request lost password
|
||||
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
|
||||
if ( !registrationSettings.EnableLostPassword ) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
if(String.IsNullOrWhiteSpace(username)){
|
||||
_orchardServices.Notifier.Error(T("Invalid username or E-mail"));
|
||||
@@ -160,17 +171,6 @@ namespace Orchard.Users.Controllers {
|
||||
return RedirectToAction("LogOn");
|
||||
}
|
||||
|
||||
public ActionResult ValidateLostPassword(string nonce) {
|
||||
IUser user;
|
||||
if (null != (user = _userService.ValidateLostPassword(nonce))) {
|
||||
_authenticationService.SignIn(user, false);
|
||||
return RedirectToAction("ChangePassword");
|
||||
}
|
||||
else {
|
||||
return new RedirectResult("~/");
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public ActionResult ChangePassword() {
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
@@ -180,9 +180,53 @@ namespace Orchard.Users.Controllers {
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
|
||||
Justification = "Exceptions result in password not being changed.")]
|
||||
public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword) {
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
if ( !ValidateChangePassword(currentPassword, newPassword, confirmPassword) ) {
|
||||
return View();
|
||||
}
|
||||
|
||||
try {
|
||||
var validated = _membershipService.ValidateUser(User.Identity.Name, currentPassword);
|
||||
|
||||
if ( validated != null ) {
|
||||
_membershipService.SetPassword(validated, newPassword);
|
||||
return RedirectToAction("ChangePasswordSuccess");
|
||||
}
|
||||
else {
|
||||
ModelState.AddModelError("_FORM",
|
||||
T("The current password is incorrect or the new password is invalid."));
|
||||
return ChangePassword();
|
||||
}
|
||||
}
|
||||
catch {
|
||||
ModelState.AddModelError("_FORM", T("The current password is incorrect or the new password is invalid."));
|
||||
return ChangePassword();
|
||||
}
|
||||
}
|
||||
|
||||
public ActionResult LostPassword(string nonce) {
|
||||
if ( _userService.ValidateLostPassword(nonce) == null ) {
|
||||
return RedirectToAction("LogOn");
|
||||
}
|
||||
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult LostPassword(string nonce, string newPassword, string confirmPassword) {
|
||||
IUser user;
|
||||
if ( (user = _userService.ValidateLostPassword(nonce)) == null ) {
|
||||
return Redirect("~/");
|
||||
}
|
||||
|
||||
ViewData["PasswordLength"] = MinPasswordLength;
|
||||
|
||||
if (newPassword == null || newPassword.Length < MinPasswordLength) {
|
||||
ModelState.AddModelError("newPassword", T("You must specify a new password of {0} or more characters.", MinPasswordLength));
|
||||
}
|
||||
@@ -195,15 +239,15 @@ namespace Orchard.Users.Controllers {
|
||||
return View();
|
||||
}
|
||||
|
||||
_membershipService.SetPassword(_orchardServices.WorkContext.CurrentUser, newPassword);
|
||||
_membershipService.SetPassword(user, newPassword);
|
||||
return RedirectToAction("ChangePasswordSuccess");
|
||||
}
|
||||
|
||||
public ActionResult RegistrationPending() {
|
||||
public ActionResult ChangePasswordSuccess() {
|
||||
return View();
|
||||
}
|
||||
|
||||
public ActionResult ChangePasswordSuccess() {
|
||||
public ActionResult RegistrationPending() {
|
||||
return View();
|
||||
}
|
||||
|
||||
@@ -237,6 +281,21 @@ namespace Orchard.Users.Controllers {
|
||||
}
|
||||
|
||||
#region Validation Methods
|
||||
private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) {
|
||||
if ( String.IsNullOrEmpty(currentPassword) ) {
|
||||
ModelState.AddModelError("currentPassword", T("You must specify a current password."));
|
||||
}
|
||||
if ( newPassword == null || newPassword.Length < MinPasswordLength ) {
|
||||
ModelState.AddModelError("newPassword", T("You must specify a new password of {0} or more characters.", MinPasswordLength));
|
||||
}
|
||||
|
||||
if ( !String.Equals(newPassword, confirmPassword, StringComparison.Ordinal) ) {
|
||||
ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match."));
|
||||
}
|
||||
|
||||
return ModelState.IsValid;
|
||||
}
|
||||
|
||||
|
||||
private IUser ValidateLogOn(string userNameOrEmail, string password) {
|
||||
bool validate = true;
|
||||
|
@@ -5,10 +5,10 @@ using Orchard.ContentManagement;
|
||||
using Orchard.Users.Models;
|
||||
|
||||
namespace Orchard.Users.Handlers {
|
||||
public class ModerationMessageAlteration : IMessageEventHandler {
|
||||
public class UserMessagesAlteration : IMessageEventHandler {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public ModerationMessageAlteration(IContentManager contentManager) {
|
||||
public UserMessagesAlteration(IContentManager contentManager) {
|
||||
_contentManager = contentManager;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
@@ -26,6 +26,7 @@ namespace Orchard.Users {
|
||||
.Column<bool>("UsersMustValidateEmail", c => c.WithDefault(false))
|
||||
.Column<bool>("UsersAreModerated", c => c.WithDefault(false))
|
||||
.Column<bool>("NotifyModeration", c => c.WithDefault(false))
|
||||
.Column<bool>("EnableLostPassword", c => c.WithDefault(false))
|
||||
);
|
||||
|
||||
return 1;
|
||||
|
@@ -23,5 +23,10 @@ namespace Orchard.Users.Models {
|
||||
set { Record.NotifyModeration = value; }
|
||||
}
|
||||
|
||||
public bool EnableLostPassword {
|
||||
get { return Record.EnableLostPassword; }
|
||||
set { Record.EnableLostPassword = value; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Mail;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Users.Models {
|
||||
public class RegistrationSettingsPartRecord : ContentPartRecord {
|
||||
@@ -8,5 +6,6 @@ namespace Orchard.Users.Models {
|
||||
public virtual bool UsersMustValidateEmail { get; set; }
|
||||
public virtual bool UsersAreModerated { get; set; }
|
||||
public virtual bool NotifyModeration { get; set; }
|
||||
public virtual bool EnableLostPassword { get; set; }
|
||||
}
|
||||
}
|
@@ -59,7 +59,7 @@
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Events\UserContext.cs" />
|
||||
<Compile Include="Handlers\ModerationMessageAlteration.cs" />
|
||||
<Compile Include="Handlers\UserMessagesAlteration.cs" />
|
||||
<Compile Include="Handlers\RegistrationSettingsPartHandler.cs" />
|
||||
<Compile Include="Events\IUserEventHandler.cs" />
|
||||
<Compile Include="Models\MessageTypes.cs" />
|
||||
@@ -74,6 +74,7 @@
|
||||
<Compile Include="Services\IUserService.cs" />
|
||||
<Compile Include="Services\MembershipService.cs" />
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Services\MissingSettingsBanner.cs" />
|
||||
<Compile Include="Services\UserService.cs" />
|
||||
<Compile Include="ViewModels\LogOnViewModel.cs" />
|
||||
<Compile Include="ViewModels\UserCreateViewModel.cs" />
|
||||
@@ -123,6 +124,9 @@
|
||||
<Content Include="Views\EditorTemplates\Parts\User.Edit.cshtml" />
|
||||
<Content Include="Views\EditorTemplates\Parts\User.Create.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Account\RequestLostPassword.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Account\LostPassword.cshtml" />
|
||||
</ItemGroup>
|
||||
|
@@ -0,0 +1,36 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.UI.Admin.Notification;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Users.Models;
|
||||
|
||||
namespace Orchard.Users.Services {
|
||||
public class MissingSettingsBanner : INotificationProvider {
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
private readonly IMessageManager _messageManager;
|
||||
|
||||
public MissingSettingsBanner(IOrchardServices orchardServices, IMessageManager messageManager) {
|
||||
_orchardServices = orchardServices;
|
||||
_messageManager = messageManager;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public IEnumerable<NotifyEntry> GetNotifications() {
|
||||
|
||||
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
|
||||
|
||||
if ( registrationSettings != null &&
|
||||
( registrationSettings.UsersMustValidateEmail ||
|
||||
registrationSettings.NotifyModeration ||
|
||||
registrationSettings.EnableLostPassword ) &&
|
||||
!_messageManager.GetAvailableChannelServices().Contains("email") ) {
|
||||
yield return new NotifyEntry { Message = T("Some Orchard.User settings require an Email channel to be enabled."), Type = NotifyType.Warning };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,6 +6,11 @@
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
<fieldset>
|
||||
<legend>@T("Account Information")</legend>
|
||||
<div>
|
||||
<label for="currentPassword">@T("Current password:")</label>
|
||||
@Html.Password("currentPassword")
|
||||
@Html.ValidationMessage("currentPassword")
|
||||
</div>
|
||||
<div>
|
||||
<label for="newPassword">@T("New password:")</label>
|
||||
@Html.Password("newPassword")
|
||||
|
@@ -3,13 +3,14 @@
|
||||
|
||||
@{
|
||||
var userCanRegister = @WorkContext.CurrentSite.As<Orchard.Users.Models.RegistrationSettingsPart>().UsersCanRegister;
|
||||
var enableLostPassword = @WorkContext.CurrentSite.As<Orchard.Users.Models.RegistrationSettingsPart>().EnableLostPassword;
|
||||
}
|
||||
|
||||
<h1 class="page-title">@Html.TitleForPage(Model.Title)</h1>
|
||||
<p>
|
||||
@T("Please enter your username and password.")
|
||||
@if(userCanRegister) { @Html.ActionLink(T("Register").Text, "Register") @T(" if you don't have an account.") }
|
||||
@Html.ActionLink(T("Lost your Password?").Text, "LostPassword")
|
||||
@if(enableLostPassword) { @Html.ActionLink(T("Lost your Password?").Text, "RequestLostPassword") }
|
||||
</p>
|
||||
@Html.ValidationSummary(T("Login was unsuccessful. Please correct the errors and try again.").ToString())
|
||||
|
||||
|
@@ -1,15 +1,23 @@
|
||||
<h1>@Html.TitleForPage(T("Lost Password").ToString())</h1>
|
||||
<p>@T("Please enter your username or email address. You will receive a link to create a new password via email.")</p>
|
||||
@model dynamic
|
||||
<h1>@Html.TitleForPage(T("Change Password").ToString()) </h1>
|
||||
<p>@T("Use the form below to change your password.")</p>
|
||||
<p>@T("New passwords are required to be a minimum of {0} characters in length.", ViewData["PasswordLength"] as string) </p>
|
||||
@Html.ValidationSummary(T("Password change was unsuccessful. Please correct the errors and try again.").ToString())
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
<fieldset>
|
||||
<legend>@T("Account Information")</legend>
|
||||
<div>
|
||||
<label for="username">@T("Username or E-mail:")</label>
|
||||
@Html.TextBox("username")
|
||||
@Html.ValidationMessage("username")
|
||||
<label for="newPassword">@T("New password:")</label>
|
||||
@Html.Password("newPassword")
|
||||
@Html.ValidationMessage("newPassword")
|
||||
</div>
|
||||
<div>
|
||||
<button class="primaryAction" type="submit">@T("Send Request")</button>
|
||||
<label for="confirmPassword">@T("Confirm new password:")</label>
|
||||
@Html.Password("confirmPassword")
|
||||
@Html.ValidationMessage("confirmPassword")
|
||||
</div>
|
||||
<div>
|
||||
<button class="primaryAction" type="submit">@T("Change Password")</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
<h1>@Html.TitleForPage(T("Lost Password").ToString())</h1>
|
||||
<p>@T("Please enter your username or email address. You will receive a link to create a new password via email.")</p>
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
<fieldset>
|
||||
<legend>@T("Account Information")</legend>
|
||||
<div>
|
||||
<label for="username">@T("Username or E-mail:")</label>
|
||||
@Html.TextBox("username")
|
||||
@Html.ValidationMessage("username")
|
||||
</div>
|
||||
<div>
|
||||
<button class="primaryAction" type="submit">@T("Send Request")</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
}
|
@@ -7,6 +7,11 @@
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor( m => m.UsersCanRegister)">@T("Users can create new accounts on the site")</label>
|
||||
@Html.ValidationMessage("UsersCanRegister", "*")
|
||||
</div>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.EnableLostPassword)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor( m => m.EnableLostPassword)">@T("Display a link to enable users to reset their email")</label>
|
||||
@Html.ValidationMessage("EnableLostPassword", "*")
|
||||
</div>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.UsersMustValidateEmail)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor( m => m.UsersMustValidateEmail)">@T("Users must verify their email address")</label>
|
||||
|
Reference in New Issue
Block a user