Display custom login errors (#7734)

Fixes #7588
This commit is contained in:
Julián Alazorza
2017-09-14 21:26:55 +02:00
committed by Sébastien Ros
parent 70adb1833f
commit 8036e539b6
12 changed files with 98 additions and 70 deletions

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Web.Security; using System.Web.Security;
using Autofac; using Autofac;
using Moq; using Moq;
@@ -21,6 +22,7 @@ using Orchard.DisplayManagement.Implementation;
using Orchard.Environment; using Orchard.Environment;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Security; using Orchard.Security;
using Orchard.Services; using Orchard.Services;
using Orchard.Settings; using Orchard.Settings;
@@ -146,8 +148,9 @@ namespace Orchard.Tests.Modules.Users.Services
Assert.That(user1Record.PasswordSalt, Is.Not.EqualTo(user2Record.PasswordSalt)); Assert.That(user1Record.PasswordSalt, Is.Not.EqualTo(user2Record.PasswordSalt));
Assert.That(user1Record.Password, Is.Not.EqualTo(user2Record.Password)); Assert.That(user1Record.Password, Is.Not.EqualTo(user2Record.Password));
Assert.That(_membershipService.ValidateUser("a", "b"), Is.Not.Null); List<LocalizedString> validationErrors;
Assert.That(_membershipService.ValidateUser("d", "b"), Is.Not.Null); Assert.That(_membershipService.ValidateUser("a", "b", out validationErrors), Is.Not.Null);
Assert.That(_membershipService.ValidateUser("d", "b", out validationErrors), Is.Not.Null);
} }
[Test] [Test]
@@ -156,9 +159,10 @@ namespace Orchard.Tests.Modules.Users.Services
_session.Flush(); _session.Flush();
_session.Clear(); _session.Clear();
var validate1 = _membershipService.ValidateUser("test-user", "bad-password"); List<LocalizedString> validationErrors;
var validate2 = _membershipService.ValidateUser("bad-user", "test-password"); var validate1 = _membershipService.ValidateUser("test-user", "bad-password", out validationErrors);
var validate3 = _membershipService.ValidateUser("test-user", "test-password"); var validate2 = _membershipService.ValidateUser("bad-user", "test-password", out validationErrors);
var validate3 = _membershipService.ValidateUser("test-user", "test-password", out validationErrors);
Assert.That(validate1, Is.Null); Assert.That(validate1, Is.Null);
Assert.That(validate2, Is.Null); Assert.That(validate2, Is.Null);

View File

@@ -18,6 +18,7 @@ using Orchard.Security;
using Orchard.Blogs.Extensions; using Orchard.Blogs.Extensions;
using Orchard.Mvc.Html; using Orchard.Mvc.Html;
using Orchard.Core.Title.Models; using Orchard.Core.Title.Models;
using System.Linq;
namespace Orchard.Blogs.Services { namespace Orchard.Blogs.Services {
[OrchardFeature("Orchard.Blogs.RemotePublishing")] [OrchardFeature("Orchard.Blogs.RemotePublishing")]
@@ -208,7 +209,7 @@ namespace Orchard.Blogs.Services {
//AutoroutePart //AutoroutePart
dynamic dBlogPost = blogPost; dynamic dBlogPost = blogPost;
if (dBlogPost.AutoroutePart!=null){ if (dBlogPost.AutoroutePart!=null) {
dBlogPost.AutoroutePart.DisplayAlias = slug; dBlogPost.AutoroutePart.DisplayAlias = slug;
} }
@@ -340,11 +341,11 @@ namespace Orchard.Blogs.Services {
} }
private IUser ValidateUser(string userName, string password) { private IUser ValidateUser(string userName, string password) {
IUser user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
if (user == null) { IUser user = _membershipService.ValidateUser(userName, password,out validationErrors);
throw new OrchardCoreException(T("The username or e-mail or password provided is incorrect.")); if (validationErrors.Any()) {
throw new OrchardCoreException(validationErrors.FirstOrDefault());
} }
return user; return user;
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing; using System.Web.Routing;
@@ -55,7 +56,8 @@ namespace Orchard.Media.Services {
XRpcStruct file, XRpcStruct file,
UrlHelper url) { UrlHelper url) {
var user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userName, password, out validationErrors);
if (!_authorizationService.TryCheckAccess(Permissions.ManageMedia, user, null)) { if (!_authorizationService.TryCheckAccess(Permissions.ManageMedia, user, null)) {
throw new OrchardCoreException(T("Access denied")); throw new OrchardCoreException(T("Access denied"));
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing; using System.Web.Routing;
@@ -59,7 +60,8 @@ namespace Orchard.MediaLibrary.Services {
XRpcStruct file, XRpcStruct file,
UrlHelper url) { UrlHelper url) {
var user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userName, password, out validationErrors);
if (!_authorizationService.TryCheckAccess(Permissions.ManageOwnMedia, user, null)) { if (!_authorizationService.TryCheckAccess(Permissions.ManageOwnMedia, user, null)) {
throw new OrchardCoreException(T("Access denied")); throw new OrchardCoreException(T("Access denied"));
} }

View File

@@ -162,7 +162,8 @@ namespace Orchard.OpenId.Controllers
if (!validate) if (!validate)
return null; return null;
var user = _membershipService.ValidateUser(userNameOrEmail, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors);
if (user == null) { if (user == null) {
ModelState.AddModelError("password", T("The username or e-mail or password provided is incorrect.")); ModelState.AddModelError("password", T("The username or e-mail or password provided is incorrect."));
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Core.Contents; using Orchard.Core.Contents;
@@ -147,9 +148,10 @@ namespace Orchard.PublishLater.Services {
} }
private IUser ValidateUser(string userName, string password) { private IUser ValidateUser(string userName, string password) {
IUser user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
if (user == null) { IUser user = _membershipService.ValidateUser(userName, password, out validationErrors);
throw new OrchardCoreException(T("The username or e-mail or password provided is incorrect.")); if (validationErrors.Any()) {
throw new OrchardCoreException(validationErrors.FirstOrDefault());
} }
return user; return user;

View File

@@ -4,6 +4,7 @@ using System.Xml.Linq;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Core.XmlRpc; using Orchard.Core.XmlRpc;
using Orchard.Core.XmlRpc.Models; using Orchard.Core.XmlRpc.Models;
using Orchard.Localization;
using Orchard.Security; using Orchard.Security;
using Orchard.Tags.Helpers; using Orchard.Tags.Helpers;
using Orchard.Tags.Models; using Orchard.Tags.Models;
@@ -88,7 +89,8 @@ namespace Orchard.Tags.Services {
if (postId < 1) if (postId < 1)
return; return;
var user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userName, password, out validationErrors);
_authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null); _authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null);
var driver = new XmlRpcDriver(item => { var driver = new XmlRpcDriver(item => {
@@ -117,7 +119,8 @@ namespace Orchard.Tags.Services {
} }
private XRpcArray MetaWeblogGetTags(string appKey, string userName, string password) { private XRpcArray MetaWeblogGetTags(string appKey, string userName, string password) {
var user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userName, password, out validationErrors);
_authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null); _authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null);
var array = new XRpcArray(); var array = new XRpcArray();
@@ -137,7 +140,8 @@ namespace Orchard.Tags.Services {
} }
private void MetaWeblogUpdateTags(int contentItemId, string userName, string password, XRpcStruct content, bool publish, ICollection<IXmlRpcDriver> drivers) { private void MetaWeblogUpdateTags(int contentItemId, string userName, string password, XRpcStruct content, bool publish, ICollection<IXmlRpcDriver> drivers) {
var user = _membershipService.ValidateUser(userName, password); List<LocalizedString> validationErrors;
var user = _membershipService.ValidateUser(userName, password, out validationErrors);
var rawTags = content.Optional<string>("mt_keywords"); var rawTags = content.Optional<string>("mt_keywords");
if (string.IsNullOrWhiteSpace(rawTags)) if (string.IsNullOrWhiteSpace(rawTags))

View File

@@ -60,8 +60,8 @@ namespace Orchard.Users.Activities {
yield return T("IncorrectUserNameOrPassword"); yield return T("IncorrectUserNameOrPassword");
yield break; yield break;
} }
List<LocalizedString> validationErrors;
user = _membershipService.ValidateUser(userNameOrEmail, password); user = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors);
} }
if (user == null) { if (user == null) {
@@ -80,7 +80,7 @@ namespace Orchard.Users.Activities {
if (String.IsNullOrWhiteSpace(value)) if (String.IsNullOrWhiteSpace(value))
return false; return false;
var falseValues = new[] {"false", "off", "no"}; var falseValues = new[] { "false", "off", "no" };
return falseValues.All(x => !String.Equals(x, value, StringComparison.OrdinalIgnoreCase)); return falseValues.All(x => !String.Equals(x, value, StringComparison.OrdinalIgnoreCase));
} }
} }

View File

@@ -17,6 +17,7 @@ using System.Web.Mvc;
using System.Web.Security; using System.Web.Security;
using Orchard.Services; using Orchard.Services;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Orchard.Users.Controllers { namespace Orchard.Users.Controllers {
[HandleError, Themed] [HandleError, Themed]
@@ -289,7 +290,8 @@ namespace Orchard.Users.Controllers {
private bool PasswordChangeIsSuccess(string currentPassword, string newPassword, string username) { private bool PasswordChangeIsSuccess(string currentPassword, string newPassword, string username) {
try { try {
var validated = _membershipService.ValidateUser(username, currentPassword); List<LocalizedString> validationErrors;
var validated = _membershipService.ValidateUser(username, currentPassword, out validationErrors);
if (validated != null) { if (validated != null) {
_membershipService.SetPassword(validated, newPassword); _membershipService.SetPassword(validated, newPassword);
@@ -311,7 +313,7 @@ namespace Orchard.Users.Controllers {
[AlwaysAccessible] [AlwaysAccessible]
public ActionResult LostPassword(string nonce) { public ActionResult LostPassword(string nonce) {
if ( _userService.ValidateLostPassword(nonce) == null ) { if ( _userService.ValidateLostPassword(nonce) == null) {
return RedirectToAction("LogOn"); return RedirectToAction("LogOn");
} }
@@ -326,7 +328,7 @@ namespace Orchard.Users.Controllers {
[ValidateInput(false)] [ValidateInput(false)]
public ActionResult LostPassword(string nonce, string newPassword, string confirmPassword) { public ActionResult LostPassword(string nonce, string newPassword, string confirmPassword) {
IUser user; IUser user;
if ( (user = _userService.ValidateLostPassword(nonce)) == null ) { if ( (user = _userService.ValidateLostPassword(nonce)) == null) {
return Redirect("~/"); return Redirect("~/");
} }
@@ -374,7 +376,7 @@ namespace Orchard.Users.Controllers {
public ActionResult ChallengeEmail(string nonce) { public ActionResult ChallengeEmail(string nonce) {
var user = _userService.ValidateChallenge(nonce); var user = _userService.ValidateChallenge(nonce);
if ( user != null ) { if ( user != null) {
_userEventHandler.ConfirmedEmail(user); _userEventHandler.ConfirmedEmail(user);
return RedirectToAction("ChallengeEmailSuccess"); return RedirectToAction("ChallengeEmailSuccess");
@@ -385,7 +387,7 @@ namespace Orchard.Users.Controllers {
#region Validation Methods #region Validation Methods
private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) { private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) {
if ( String.IsNullOrEmpty(currentPassword) ) { if ( String.IsNullOrEmpty(currentPassword)) {
ModelState.AddModelError("currentPassword", T("You must specify a current password.")); ModelState.AddModelError("currentPassword", T("You must specify a current password."));
} }
@@ -395,7 +397,7 @@ namespace Orchard.Users.Controllers {
ValidatePassword(newPassword); ValidatePassword(newPassword);
if ( !String.Equals(newPassword, confirmPassword, StringComparison.Ordinal) ) { if ( !String.Equals(newPassword, confirmPassword, StringComparison.Ordinal)) {
ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match.")); ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match."));
} }
@@ -418,13 +420,17 @@ namespace Orchard.Users.Controllers {
if (!validate) if (!validate)
return null; return null;
var user = _membershipService.ValidateUser(userNameOrEmail, password); List<LocalizedString> validationErrors;
if (user == null) { var validationResult = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors);
if (validationResult == null) {
_userEventHandler.LogInFailed(userNameOrEmail, password); _userEventHandler.LogInFailed(userNameOrEmail, password);
ModelState.AddModelError("_FORM", T("The username or e-mail or password provided is incorrect."));
} }
return user; foreach (var error in validationErrors) {
ModelState.AddModelError("_FORM", error);
}
return validationResult;
} }
private bool ValidateRegistration(string userName, string email, string password, string confirmPassword) { private bool ValidateRegistration(string userName, string email, string password, string confirmPassword) {

View File

@@ -74,7 +74,7 @@ namespace Orchard.Users.Services {
user.CreatedUtc = _clock.UtcNow; user.CreatedUtc = _clock.UtcNow;
SetPassword(user, createUserParams.Password); SetPassword(user, createUserParams.Password);
if ( registrationSettings != null ) { if ( registrationSettings != null) {
user.RegistrationStatus = registrationSettings.UsersAreModerated ? UserStatus.Pending : UserStatus.Approved; user.RegistrationStatus = registrationSettings.UsersAreModerated ? UserStatus.Pending : UserStatus.Approved;
user.EmailStatus = registrationSettings.UsersMustValidateEmail ? UserStatus.Pending : UserStatus.Approved; user.EmailStatus = registrationSettings.UsersMustValidateEmail ? UserStatus.Pending : UserStatus.Approved;
} }
@@ -84,7 +84,7 @@ namespace Orchard.Users.Services {
user.EmailStatus = UserStatus.Approved; user.EmailStatus = UserStatus.Approved;
} }
var userContext = new UserContext {User = user, Cancel = false, UserParameters = createUserParams}; var userContext = new UserContext { User = user, Cancel = false, UserParameters = createUserParams };
_userEventHandlers.Creating(userContext); _userEventHandlers.Creating(userContext);
if(userContext.Cancel) { if(userContext.Cancel) {
@@ -101,7 +101,7 @@ namespace Orchard.Users.Services {
if ( registrationSettings != null if ( registrationSettings != null
&& registrationSettings.UsersAreModerated && registrationSettings.UsersAreModerated
&& registrationSettings.NotifyModeration && registrationSettings.NotifyModeration
&& !createUserParams.IsApproved ) { && !createUserParams.IsApproved) {
var usernames = String.IsNullOrWhiteSpace(registrationSettings.NotificationsRecipients) var usernames = String.IsNullOrWhiteSpace(registrationSettings.NotificationsRecipients)
? new string[0] ? new string[0]
: registrationSettings.NotificationsRecipients.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries); : registrationSettings.NotificationsRecipients.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
@@ -135,27 +135,28 @@ namespace Orchard.Users.Services {
return _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); return _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault();
} }
public IUser ValidateUser(string userNameOrEmail, string password) { public IUser ValidateUser(string userNameOrEmail, string password, out List<LocalizedString> validationErrors) {
var lowerName = userNameOrEmail == null ? "" : userNameOrEmail.ToLowerInvariant(); var lowerName = userNameOrEmail == null ? "" : userNameOrEmail.ToLowerInvariant();
validationErrors = new List<LocalizedString>();
var user = _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); var user = _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault();
if (user == null) if (user == null)
user = _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.Email == lowerName).List().FirstOrDefault(); user = _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.Email == lowerName).List().FirstOrDefault();
if ( user == null || ValidatePassword(user.As<UserPart>(), password) == false ) if (user == null || ValidatePassword(user.As<UserPart>(), password) == false) {
validationErrors.Add(T("The username or e-mail or password provided is incorrect."));
return null; return null;
}
if ( user.EmailStatus != UserStatus.Approved ) if (user.EmailStatus != UserStatus.Approved)
return null; validationErrors.Add(T("You must verify your email"));
if ( user.RegistrationStatus != UserStatus.Approved ) if (user.RegistrationStatus != UserStatus.Approved)
return null; validationErrors.Add(T("You must be approved before being able to login"));
return user; return user;
} }
public bool PasswordIsExpired(IUser user, int days){ public bool PasswordIsExpired(IUser user, int days) {
return user.As<UserPart>().LastPasswordChangeUtc.Value.AddDays(days) < _clock.UtcNow; return user.As<UserPart>().LastPasswordChangeUtc.Value.AddDays(days) < _clock.UtcNow;
} }

View File

@@ -1,10 +1,13 @@
namespace Orchard.Security { using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Security {
public interface IMembershipService : IDependency { public interface IMembershipService : IDependency {
IMembershipSettings GetSettings(); IMembershipSettings GetSettings();
IUser CreateUser(CreateUserParams createUserParams); IUser CreateUser(CreateUserParams createUserParams);
IUser GetUser(string username); IUser GetUser(string username);
IUser ValidateUser(string userNameOrEmail, string password); IUser ValidateUser(string userNameOrEmail, string password, out List<LocalizedString> validationErrors);
void SetPassword(IUser user, string password); void SetPassword(IUser user, string password);
bool PasswordIsExpired(IUser user, int days); bool PasswordIsExpired(IUser user, int days);

View File

@@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Security { namespace Orchard.Security {
/// <summary> /// <summary>
@@ -23,7 +25,7 @@ namespace Orchard.Security {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IUser ValidateUser(string userNameOrEmail, string password) { public IUser ValidateUser(string userNameOrEmail, string password, out List<LocalizedString> validationErrors) {
throw new NotImplementedException(); throw new NotImplementedException();
} }