diff --git a/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs index 34889b31b..54bdb9530 100644 --- a/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs +++ b/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Web.Security; using Autofac; using Moq; @@ -21,6 +22,7 @@ using Orchard.DisplayManagement.Implementation; using Orchard.Environment; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; +using Orchard.Localization; using Orchard.Security; using Orchard.Services; using Orchard.Settings; @@ -32,7 +34,7 @@ using Orchard.Users.Handlers; using Orchard.Users.Models; using Orchard.Users.Services; -namespace Orchard.Tests.Modules.Users.Services +namespace Orchard.Tests.Modules.Users.Services { [TestFixture] public class MembershipServiceTests { @@ -146,8 +148,9 @@ namespace Orchard.Tests.Modules.Users.Services Assert.That(user1Record.PasswordSalt, Is.Not.EqualTo(user2Record.PasswordSalt)); Assert.That(user1Record.Password, Is.Not.EqualTo(user2Record.Password)); - Assert.That(_membershipService.ValidateUser("a", "b"), Is.Not.Null); - Assert.That(_membershipService.ValidateUser("d", "b"), Is.Not.Null); + List validationErrors; + Assert.That(_membershipService.ValidateUser("a", "b", out validationErrors), Is.Not.Null); + Assert.That(_membershipService.ValidateUser("d", "b", out validationErrors), Is.Not.Null); } [Test] @@ -156,9 +159,10 @@ namespace Orchard.Tests.Modules.Users.Services _session.Flush(); _session.Clear(); - var validate1 = _membershipService.ValidateUser("test-user", "bad-password"); - var validate2 = _membershipService.ValidateUser("bad-user", "test-password"); - var validate3 = _membershipService.ValidateUser("test-user", "test-password"); + List validationErrors; + var validate1 = _membershipService.ValidateUser("test-user", "bad-password", out validationErrors); + 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(validate2, Is.Null); @@ -168,7 +172,7 @@ namespace Orchard.Tests.Modules.Users.Services [Test] public void UsersWhoHaveNeverLoggedInCanBeAuthenticated() { var user = (UserPart)_membershipService.CreateUser(new CreateUserParams("a", "b", "c", null, null, true)); - + Assert.That(_membershipValidationService.CanAuthenticateWithCookie(user), Is.True); } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs index 99fdcf832..f2d49ab0c 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs @@ -18,6 +18,7 @@ using Orchard.Security; using Orchard.Blogs.Extensions; using Orchard.Mvc.Html; using Orchard.Core.Title.Models; +using System.Linq; namespace Orchard.Blogs.Services { [OrchardFeature("Orchard.Blogs.RemotePublishing")] @@ -30,7 +31,7 @@ namespace Orchard.Blogs.Services { private readonly RouteCollection _routeCollection; public XmlRpcHandler(IBlogService blogService, IBlogPostService blogPostService, IContentManager contentManager, - IAuthorizationService authorizationService, IMembershipService membershipService, + IAuthorizationService authorizationService, IMembershipService membershipService, RouteCollection routeCollection) { _blogService = blogService; _blogPostService = blogPostService; @@ -205,10 +206,10 @@ namespace Orchard.Blogs.Services { if (blogPost.Is()) { blogPost.As().Title = HttpUtility.HtmlDecode(title); } - + //AutoroutePart dynamic dBlogPost = blogPost; - if (dBlogPost.AutoroutePart!=null){ + if (dBlogPost.AutoroutePart!=null) { dBlogPost.AutoroutePart.DisplayAlias = slug; } @@ -340,11 +341,11 @@ namespace Orchard.Blogs.Services { } private IUser ValidateUser(string userName, string password) { - IUser user = _membershipService.ValidateUser(userName, password); - if (user == null) { - throw new OrchardCoreException(T("The username or e-mail or password provided is incorrect.")); + List validationErrors; + IUser user = _membershipService.ValidateUser(userName, password,out validationErrors); + if (validationErrors.Any()) { + throw new OrchardCoreException(validationErrors.FirstOrDefault()); } - return user; } @@ -361,13 +362,13 @@ namespace Orchard.Blogs.Services { var blogStruct = new XRpcStruct() .Set("postid", blogPostPart.Id) .Set("title", HttpUtility.HtmlEncode(blogPostPart.Title)) - + .Set("description", blogPostPart.Text) .Set("link", url) .Set("permaLink", url); - + blogStruct.Set("wp_slug", blogPostPart.As().Path); - + if (blogPostPart.PublishedUtc != null) { blogStruct.Set("dateCreated", blogPostPart.PublishedUtc); diff --git a/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs index c6d6cd743..893698cc1 100644 --- a/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Web.Mvc; using System.Web.Routing; @@ -55,7 +56,8 @@ namespace Orchard.Media.Services { XRpcStruct file, UrlHelper url) { - var user = _membershipService.ValidateUser(userName, password); + List validationErrors; + var user = _membershipService.ValidateUser(userName, password, out validationErrors); if (!_authorizationService.TryCheckAccess(Permissions.ManageMedia, user, null)) { throw new OrchardCoreException(T("Access denied")); } diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/XmlRpcHandler.cs index 14a35ab00..03692b803 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Services/XmlRpcHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Web.Mvc; using System.Web.Routing; @@ -59,7 +60,8 @@ namespace Orchard.MediaLibrary.Services { XRpcStruct file, UrlHelper url) { - var user = _membershipService.ValidateUser(userName, password); + List validationErrors; + var user = _membershipService.ValidateUser(userName, password, out validationErrors); if (!_authorizationService.TryCheckAccess(Permissions.ManageOwnMedia, user, null)) { throw new OrchardCoreException(T("Access denied")); } diff --git a/src/Orchard.Web/Modules/Orchard.OpenId/Controllers/AccountController.cs b/src/Orchard.Web/Modules/Orchard.OpenId/Controllers/AccountController.cs index f528cd813..daba1c4c0 100644 --- a/src/Orchard.Web/Modules/Orchard.OpenId/Controllers/AccountController.cs +++ b/src/Orchard.Web/Modules/Orchard.OpenId/Controllers/AccountController.cs @@ -162,7 +162,8 @@ namespace Orchard.OpenId.Controllers if (!validate) return null; - var user = _membershipService.ValidateUser(userNameOrEmail, password); + List validationErrors; + var user = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors); if (user == null) { ModelState.AddModelError("password", T("The username or e-mail or password provided is incorrect.")); } @@ -170,8 +171,8 @@ namespace Orchard.OpenId.Controllers return user; } - private string GetCallbackPath(WorkContext workContext) - { + private string GetCallbackPath(WorkContext workContext) + { var shellSettings = workContext.Resolve(); var tenantPrefix = shellSettings.RequestUrlPrefix; diff --git a/src/Orchard.Web/Modules/Orchard.PublishLater/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.PublishLater/Services/XmlRpcHandler.cs index dfc15774a..210879364 100644 --- a/src/Orchard.Web/Modules/Orchard.PublishLater/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.PublishLater/Services/XmlRpcHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Linq; using Orchard.ContentManagement; using Orchard.Core.Contents; @@ -147,9 +148,10 @@ namespace Orchard.PublishLater.Services { } private IUser ValidateUser(string userName, string password) { - IUser user = _membershipService.ValidateUser(userName, password); - if (user == null) { - throw new OrchardCoreException(T("The username or e-mail or password provided is incorrect.")); + List validationErrors; + IUser user = _membershipService.ValidateUser(userName, password, out validationErrors); + if (validationErrors.Any()) { + throw new OrchardCoreException(validationErrors.FirstOrDefault()); } return user; diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.Tags/Services/XmlRpcHandler.cs index a61f209fd..14f319930 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Services/XmlRpcHandler.cs @@ -4,6 +4,7 @@ using System.Xml.Linq; using Orchard.ContentManagement; using Orchard.Core.XmlRpc; using Orchard.Core.XmlRpc.Models; +using Orchard.Localization; using Orchard.Security; using Orchard.Tags.Helpers; using Orchard.Tags.Models; @@ -88,7 +89,8 @@ namespace Orchard.Tags.Services { if (postId < 1) return; - var user = _membershipService.ValidateUser(userName, password); + List validationErrors; + var user = _membershipService.ValidateUser(userName, password, out validationErrors); _authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null); var driver = new XmlRpcDriver(item => { @@ -117,7 +119,8 @@ namespace Orchard.Tags.Services { } private XRpcArray MetaWeblogGetTags(string appKey, string userName, string password) { - var user = _membershipService.ValidateUser(userName, password); + List validationErrors; + var user = _membershipService.ValidateUser(userName, password, out validationErrors); _authorizationService.CheckAccess(StandardPermissions.AccessAdminPanel, user, null); var array = new XRpcArray(); @@ -127,17 +130,18 @@ namespace Orchard.Tags.Services { .Set("tag_id", thisTag.TagName) .Set("name", thisTag.TagName)); // nyi - not yet implemented - //.Set("count", "") - //.Set("slug", "") - //.Set("html_url", "") - //.Set("rss_url", "")); + //.Set("count", "") + //.Set("slug", "") + //.Set("html_url", "") + //.Set("rss_url", "")); } return array; } private void MetaWeblogUpdateTags(int contentItemId, string userName, string password, XRpcStruct content, bool publish, ICollection drivers) { - var user = _membershipService.ValidateUser(userName, password); + List validationErrors; + var user = _membershipService.ValidateUser(userName, password, out validationErrors); var rawTags = content.Optional("mt_keywords"); if (string.IsNullOrWhiteSpace(rawTags)) diff --git a/src/Orchard.Web/Modules/Orchard.Users/Activities/SignInUserActivity.cs b/src/Orchard.Web/Modules/Orchard.Users/Activities/SignInUserActivity.cs index bc026f3ec..f1ffddb53 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Activities/SignInUserActivity.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Activities/SignInUserActivity.cs @@ -60,8 +60,8 @@ namespace Orchard.Users.Activities { yield return T("IncorrectUserNameOrPassword"); yield break; } - - user = _membershipService.ValidateUser(userNameOrEmail, password); + List validationErrors; + user = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors); } if (user == null) { @@ -71,7 +71,7 @@ namespace Orchard.Users.Activities { _userEventHandler.LoggingIn(userNameOrEmail, password); _authenticationService.SignIn(user, createPersistentCookie); - _userEventHandler.LoggedIn(user); + _userEventHandler.LoggedIn(user); yield return T("Done"); } @@ -80,7 +80,7 @@ namespace Orchard.Users.Activities { if (String.IsNullOrWhiteSpace(value)) return false; - var falseValues = new[] {"false", "off", "no"}; + var falseValues = new[] { "false", "off", "no" }; return falseValues.All(x => !String.Equals(x, value, StringComparison.OrdinalIgnoreCase)); } } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs index 271b4edc2..bd30e7ed3 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs @@ -17,6 +17,7 @@ using System.Web.Mvc; using System.Web.Security; using Orchard.Services; using System.Collections.Generic; +using System.Linq; namespace Orchard.Users.Controllers { [HandleError, Themed] @@ -289,7 +290,8 @@ namespace Orchard.Users.Controllers { private bool PasswordChangeIsSuccess(string currentPassword, string newPassword, string username) { try { - var validated = _membershipService.ValidateUser(username, currentPassword); + List validationErrors; + var validated = _membershipService.ValidateUser(username, currentPassword, out validationErrors); if (validated != null) { _membershipService.SetPassword(validated, newPassword); @@ -311,7 +313,7 @@ namespace Orchard.Users.Controllers { [AlwaysAccessible] public ActionResult LostPassword(string nonce) { - if ( _userService.ValidateLostPassword(nonce) == null ) { + if ( _userService.ValidateLostPassword(nonce) == null) { return RedirectToAction("LogOn"); } @@ -326,7 +328,7 @@ namespace Orchard.Users.Controllers { [ValidateInput(false)] public ActionResult LostPassword(string nonce, string newPassword, string confirmPassword) { IUser user; - if ( (user = _userService.ValidateLostPassword(nonce)) == null ) { + if ( (user = _userService.ValidateLostPassword(nonce)) == null) { return Redirect("~/"); } @@ -374,7 +376,7 @@ namespace Orchard.Users.Controllers { public ActionResult ChallengeEmail(string nonce) { var user = _userService.ValidateChallenge(nonce); - if ( user != null ) { + if ( user != null) { _userEventHandler.ConfirmedEmail(user); return RedirectToAction("ChallengeEmailSuccess"); @@ -385,7 +387,7 @@ namespace Orchard.Users.Controllers { #region Validation Methods 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.")); } @@ -395,7 +397,7 @@ namespace Orchard.Users.Controllers { 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.")); } @@ -418,13 +420,17 @@ namespace Orchard.Users.Controllers { if (!validate) return null; - var user = _membershipService.ValidateUser(userNameOrEmail, password); - if (user == null) { + List validationErrors; + var validationResult = _membershipService.ValidateUser(userNameOrEmail, password, out validationErrors); + if (validationResult == null) { _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) { diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs index c363c2829..284780b89 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs @@ -33,10 +33,10 @@ namespace Orchard.Users.Services { private readonly IClock _clock; public MembershipService( - IOrchardServices orchardServices, - IMessageService messageService, - IUserEventHandler userEventHandlers, - IClock clock, + IOrchardServices orchardServices, + IMessageService messageService, + IUserEventHandler userEventHandlers, + IClock clock, IEncryptionService encryptionService, IShapeFactory shapeFactory, IShapeDisplay shapeDisplay, @@ -74,7 +74,7 @@ namespace Orchard.Users.Services { user.CreatedUtc = _clock.UtcNow; SetPassword(user, createUserParams.Password); - if ( registrationSettings != null ) { + if ( registrationSettings != null) { user.RegistrationStatus = registrationSettings.UsersAreModerated ? UserStatus.Pending : UserStatus.Approved; user.EmailStatus = registrationSettings.UsersMustValidateEmail ? UserStatus.Pending : UserStatus.Approved; } @@ -84,7 +84,7 @@ namespace Orchard.Users.Services { 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); if(userContext.Cancel) { @@ -98,10 +98,10 @@ namespace Orchard.Users.Services { _userEventHandlers.Approved(user); } - if ( registrationSettings != null - && registrationSettings.UsersAreModerated - && registrationSettings.NotifyModeration - && !createUserParams.IsApproved ) { + if ( registrationSettings != null + && registrationSettings.UsersAreModerated + && registrationSettings.NotifyModeration + && !createUserParams.IsApproved) { var usernames = String.IsNullOrWhiteSpace(registrationSettings.NotificationsRecipients) ? new string[0] : registrationSettings.NotificationsRecipients.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries); @@ -135,27 +135,28 @@ namespace Orchard.Users.Services { return _orchardServices.ContentManager.Query().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); } - public IUser ValidateUser(string userNameOrEmail, string password) { + public IUser ValidateUser(string userNameOrEmail, string password, out List validationErrors) { var lowerName = userNameOrEmail == null ? "" : userNameOrEmail.ToLowerInvariant(); - + validationErrors = new List(); var user = _orchardServices.ContentManager.Query().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); - if (user == null) user = _orchardServices.ContentManager.Query().Where(u => u.Email == lowerName).List().FirstOrDefault(); - if ( user == null || ValidatePassword(user.As(), password) == false ) + if (user == null || ValidatePassword(user.As(), password) == false) { + validationErrors.Add(T("The username or e-mail or password provided is incorrect.")); return null; + } - if ( user.EmailStatus != UserStatus.Approved ) - return null; + if (user.EmailStatus != UserStatus.Approved) + validationErrors.Add(T("You must verify your email")); - if ( user.RegistrationStatus != UserStatus.Approved ) - return null; + if (user.RegistrationStatus != UserStatus.Approved) + validationErrors.Add(T("You must be approved before being able to login")); return user; } - public bool PasswordIsExpired(IUser user, int days){ + public bool PasswordIsExpired(IUser user, int days) { return user.As().LastPasswordChangeUtc.Value.AddDays(days) < _clock.UtcNow; } @@ -227,7 +228,7 @@ namespace Orchard.Users.Services { isValid = Crypto.VerifyHashedPassword(userPart.Password, Encoding.Unicode.GetString(CombineSaltAndPassword(saltBytes, password))); } else { - isValid = SecureStringEquality(userPart.Password, ComputeHashBase64(userPart.HashAlgorithm, saltBytes, password)); + isValid = SecureStringEquality(userPart.Password, ComputeHashBase64(userPart.HashAlgorithm, saltBytes, password)); } // Migrating older password hashes to Default algorithm if necessary and enabled. @@ -235,7 +236,7 @@ namespace Orchard.Users.Services { var keepOldConfiguration = _appConfigurationAccessor.GetConfiguration("Orchard.Users.KeepOldPasswordHash"); if (String.IsNullOrEmpty(keepOldConfiguration) || keepOldConfiguration.Equals("false", StringComparison.OrdinalIgnoreCase)) { userPart.HashAlgorithm = DefaultHashAlgorithm; - userPart.Password = ComputeHashBase64(userPart.HashAlgorithm, saltBytes, password); + userPart.Password = ComputeHashBase64(userPart.HashAlgorithm, saltBytes, password); } } diff --git a/src/Orchard/Security/IMembershipService.cs b/src/Orchard/Security/IMembershipService.cs index 6c3301728..c6522c528 100644 --- a/src/Orchard/Security/IMembershipService.cs +++ b/src/Orchard/Security/IMembershipService.cs @@ -1,10 +1,13 @@ -namespace Orchard.Security { +using System.Collections.Generic; +using Orchard.Localization; + +namespace Orchard.Security { public interface IMembershipService : IDependency { IMembershipSettings GetSettings(); IUser CreateUser(CreateUserParams createUserParams); IUser GetUser(string username); - IUser ValidateUser(string userNameOrEmail, string password); + IUser ValidateUser(string userNameOrEmail, string password, out List validationErrors); void SetPassword(IUser user, string password); bool PasswordIsExpired(IUser user, int days); diff --git a/src/Orchard/Security/NullMembershipService.cs b/src/Orchard/Security/NullMembershipService.cs index 08640b245..730ab44d5 100644 --- a/src/Orchard/Security/NullMembershipService.cs +++ b/src/Orchard/Security/NullMembershipService.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using Orchard.Localization; namespace Orchard.Security { /// @@ -23,7 +25,7 @@ namespace Orchard.Security { throw new NotImplementedException(); } - public IUser ValidateUser(string userNameOrEmail, string password) { + public IUser ValidateUser(string userNameOrEmail, string password, out List validationErrors) { throw new NotImplementedException(); }