diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs index 010b3dcf7..e9b6c83bb 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs @@ -231,13 +231,14 @@ namespace Orchard.Tests.Modules.Users.Controllers { public void ResetPasswordLinkShouldBeSent() { var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); 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()); Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("LogOn")); diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs index 3251b9105..85d5cc320 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs @@ -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(); + 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(); + 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; diff --git a/src/Orchard.Web/Modules/Orchard.Users/Handlers/ModerationMessageAlteration.cs b/src/Orchard.Web/Modules/Orchard.Users/Handlers/UserMessagesAlteration.cs similarity index 89% rename from src/Orchard.Web/Modules/Orchard.Users/Handlers/ModerationMessageAlteration.cs rename to src/Orchard.Web/Modules/Orchard.Users/Handlers/UserMessagesAlteration.cs index 932e4eacf..19c84262e 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Handlers/ModerationMessageAlteration.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Handlers/UserMessagesAlteration.cs @@ -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; } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs index 7c4725cd8..fecbac8a4 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs @@ -26,6 +26,7 @@ namespace Orchard.Users { .Column("UsersMustValidateEmail", c => c.WithDefault(false)) .Column("UsersAreModerated", c => c.WithDefault(false)) .Column("NotifyModeration", c => c.WithDefault(false)) + .Column("EnableLostPassword", c => c.WithDefault(false)) ); return 1; diff --git a/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPart.cs index 2992b1d2a..b81af213d 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPart.cs @@ -23,5 +23,10 @@ namespace Orchard.Users.Models { set { Record.NotifyModeration = value; } } + public bool EnableLostPassword { + get { return Record.EnableLostPassword; } + set { Record.EnableLostPassword = value; } + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPartRecord.cs b/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPartRecord.cs index 3a2c5d3f2..ee6a5efea 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPartRecord.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Models/RegistrationSettingsPartRecord.cs @@ -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; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj b/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj index 73a4fa092..a61a69717 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj +++ b/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj @@ -59,7 +59,7 @@ - + @@ -74,6 +74,7 @@ + @@ -123,6 +124,9 @@ + + + diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MissingSettingsBanner.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MissingSettingsBanner.cs new file mode 100644 index 000000000..b5a772951 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MissingSettingsBanner.cs @@ -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 GetNotifications() { + + var registrationSettings = _orchardServices.WorkContext.CurrentSite.As(); + + 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 }; + } + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/ChangePassword.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/ChangePassword.cshtml index 3a3b59413..592bbaf25 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/ChangePassword.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/ChangePassword.cshtml @@ -6,6 +6,11 @@ @using (Html.BeginFormAntiForgeryPost()) {
@T("Account Information") +
+ + @Html.Password("currentPassword") + @Html.ValidationMessage("currentPassword") +
@Html.Password("newPassword") diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.cshtml index f1efc967a..8fd148678 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LogOn.cshtml @@ -3,13 +3,14 @@ @{ var userCanRegister = @WorkContext.CurrentSite.As().UsersCanRegister; + var enableLostPassword = @WorkContext.CurrentSite.As().EnableLostPassword; }

@Html.TitleForPage(Model.Title)

@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") }

@Html.ValidationSummary(T("Login was unsuccessful. Please correct the errors and try again.").ToString()) diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LostPassword.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LostPassword.cshtml index 0841de7e6..3a3b59413 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LostPassword.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/LostPassword.cshtml @@ -1,15 +1,23 @@ -

@Html.TitleForPage(T("Lost Password").ToString())

-

@T("Please enter your username or email address. You will receive a link to create a new password via email.")

+@model dynamic +

@Html.TitleForPage(T("Change Password").ToString())

+

@T("Use the form below to change your password.")

+

@T("New passwords are required to be a minimum of {0} characters in length.", ViewData["PasswordLength"] as string)

+@Html.ValidationSummary(T("Password change was unsuccessful. Please correct the errors and try again.").ToString()) @using (Html.BeginFormAntiForgeryPost()) {
@T("Account Information")
- - @Html.TextBox("username") - @Html.ValidationMessage("username") + + @Html.Password("newPassword") + @Html.ValidationMessage("newPassword")
- + + @Html.Password("confirmPassword") + @Html.ValidationMessage("confirmPassword") +
+
+
} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Account/RequestLostPassword.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/RequestLostPassword.cshtml new file mode 100644 index 000000000..0841de7e6 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Account/RequestLostPassword.cshtml @@ -0,0 +1,15 @@ +

@Html.TitleForPage(T("Lost Password").ToString())

+

@T("Please enter your username or email address. You will receive a link to create a new password via email.")

+@using (Html.BeginFormAntiForgeryPost()) { +
+ @T("Account Information") +
+ + @Html.TextBox("username") + @Html.ValidationMessage("username") +
+
+ +
+
+ } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/EditorTemplates/Parts/Users.RegistrationSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/EditorTemplates/Parts/Users.RegistrationSettings.cshtml index c839341bf..6c8230863 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/EditorTemplates/Parts/Users.RegistrationSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/EditorTemplates/Parts/Users.RegistrationSettings.cshtml @@ -7,6 +7,11 @@ @Html.ValidationMessage("UsersCanRegister", "*")
+
+ @Html.EditorFor(m => m.EnableLostPassword) + + @Html.ValidationMessage("EnableLostPassword", "*") +
@Html.EditorFor(m => m.UsersMustValidateEmail)