From 414b65b269d230fe67ac7e5df44d852510b56aa5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Dec 2013 14:01:10 +1100 Subject: [PATCH] More WIP on the membership providers - both new and old. They are now both working with the same up-to-date logic and base classes. Updated the interface definitions on the member services to support the membership provider queries - now to implement the logic. --- .../Querying/ValuePropertyMatchType.cs | 14 + .../Security/MembershipProviderBase.cs | 108 +++-- src/Umbraco.Core/Services/IMemberService.cs | 73 ++- .../Services/IMembershipMemberService.cs | 20 + src/Umbraco.Core/Services/MemberCountType.cs | 13 + src/Umbraco.Core/Services/MemberService.cs | 26 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 + .../Membership/MembershipProviderBaseTests.cs | 30 +- .../Providers/MembersMembershipProvider.cs | 449 +++++++++--------- .../UsersMembershipProvider.cs | 28 +- .../members/UmbracoMembershipProvider.cs | 370 ++++++++------- 11 files changed, 668 insertions(+), 465 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs create mode 100644 src/Umbraco.Core/Services/MemberCountType.cs diff --git a/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs new file mode 100644 index 0000000000..f0c70a069d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Querying/ValuePropertyMatchType.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Core.Persistence.Querying +{ + /// + /// Determine how to match a number or data value + /// + public enum ValuePropertyMatchType + { + Exact, + GreaterThan, + LessThan, + GreaterThanOrEqualTo, + LessThanOrEqualTo + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index 24a0e29409..9d76c8e595 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -18,40 +18,10 @@ namespace Umbraco.Core.Security { //Set the defaults! DefaultMemberTypeAlias = "Member"; - LockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut; - LastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate; - FailedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts; - ApprovedPropertyTypeAlias = Constants.Conventions.Member.IsApproved; - CommentPropertyTypeAlias = Constants.Conventions.Member.Comments; - LastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate; - LastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate; - PasswordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion; - PasswordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer; } public string DefaultMemberTypeAlias { get; protected set; } - public string LockPropertyTypeAlias { get; protected set; } - public string LastLockedOutPropertyTypeAlias { get; protected set; } - public string FailedPasswordAttemptsPropertyTypeAlias { get; protected set; } - public string ApprovedPropertyTypeAlias { get; protected set; } - public string CommentPropertyTypeAlias { get; protected set; } - public string LastLoginPropertyTypeAlias { get; protected set; } - public string LastPasswordChangedPropertyTypeAlias { get; protected set; } - public string PasswordRetrievalQuestionPropertyTypeAlias { get; protected set; } - public string PasswordRetrievalAnswerPropertyTypeAlias { get; protected set; } - - public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) - { - base.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); - - if (string.IsNullOrEmpty(PasswordRetrievalQuestionPropertyTypeAlias) || string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias)) - { - throw new NotSupportedException("Updating the password Question and Answer is not valid if the properties aren't set in the config file"); - } - - return PerformChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); - } - + /// /// Adds a new membership user to the data source. /// @@ -398,6 +368,21 @@ namespace Umbraco.Core.Security throw new NotSupportedException("This provider does not support manually changing the password"); } + var args = new ValidatePasswordEventArgs(username, newPassword, false); + OnValidatingPassword(args); + + if (args.Cancel) + { + if (args.FailureInformation != null) + throw args.FailureInformation; + throw new MembershipPasswordException("Change password canceled due to password validation failure."); + } + + if (AllowManuallyChangingPassword == false) + { + if (ValidateUser(username, oldPassword) == false) return false; + } + return PerformChangePassword(username, oldPassword, newPassword); } @@ -431,10 +416,13 @@ namespace Umbraco.Core.Security { throw new NotSupportedException("Updating the password Question and Answer is not available if requiresQuestionAndAnswer is not set in web.config"); } - - if (ValidateUser(username, password) == false) + + if (AllowManuallyChangingPassword == false) { - return false; + if (ValidateUser(username, password) == false) + { + return false; + } } return PerformChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); @@ -534,6 +522,56 @@ namespace Umbraco.Core.Security /// protected abstract MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); + /// + /// Gets the members password if password retreival is enabled + /// + /// + /// + /// + public sealed override string GetPassword(string username, string answer) + { + if (EnablePasswordRetrieval == false) + throw new ProviderException("Password Retrieval Not Enabled."); + + if (PasswordFormat == MembershipPasswordFormat.Hashed) + throw new ProviderException("Cannot retrieve Hashed passwords."); + + return PerformGetPassword(username, answer); + } + + /// + /// Gets the members password if password retreival is enabled + /// + /// + /// + /// + protected abstract string PerformGetPassword(string username, string answer); + + public sealed override string ResetPassword(string username, string answer) + { + if (EnablePasswordReset == false) + { + throw new NotSupportedException("Password reset is not supported"); + } + + var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); + + var args = new ValidatePasswordEventArgs(username, newPassword, true); + OnValidatingPassword(args); + if (args.Cancel) + { + if (args.FailureInformation != null) + { + throw args.FailureInformation; + } + throw new MembershipPasswordException("Reset password canceled due to password validation failure."); + } + + return PerformResetPassword(username, answer, newPassword); + } + + protected abstract string PerformResetPassword(string username, string answer, string generatedPassword); + protected internal static Attempt IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength) { if (minRequiredNonAlphanumericChars > 0) @@ -561,7 +599,7 @@ namespace Umbraco.Core.Security return Attempt.Succeed(PasswordValidityError.Ok); } - + /// /// Gets the name of the default app. /// diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index 0be5211532..87c15573c6 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -17,18 +17,87 @@ namespace Umbraco.Core.Services /// bool Exists(int id); + /// + /// Get a member by its id + /// + /// + /// IMember GetById(int id); + + /// + /// Get a member by the unique key + /// + /// + /// IMember GetByKey(Guid id); + + /// + /// Get all members for the member type alias + /// + /// + /// IEnumerable GetMembersByMemberType(string memberTypeAlias); + + /// + /// Get all members for the member type id + /// + /// + /// IEnumerable GetMembersByMemberType(int memberTypeId); + + /// + /// Get all members in the member group name specified + /// + /// + /// IEnumerable GetMembersByGroup(string memberGroupName); + + /// + /// Get all members with the ids specified + /// + /// + /// IEnumerable GetAllMembers(params int[] ids); + /// + /// Delete members of the specified member type id + /// + /// void DeleteMembersOfType(int memberTypeId); + /// + /// Get members based on a property search + /// + /// + /// + /// + /// IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, string value, StringPropertyMatchType matchType = StringPropertyMatchType.Exact); - IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, int value); + + /// + /// Get members based on a property search + /// + /// + /// + /// + /// + IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, int value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact); + + /// + /// Get members based on a property search + /// + /// + /// + /// IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, bool value); - IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value); + + /// + /// Get members based on a property search + /// + /// + /// + /// + /// + IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMembershipMemberService.cs b/src/Umbraco.Core/Services/IMembershipMemberService.cs index 18edae2bea..715f85f3fb 100644 --- a/src/Umbraco.Core/Services/IMembershipMemberService.cs +++ b/src/Umbraco.Core/Services/IMembershipMemberService.cs @@ -31,6 +31,11 @@ namespace Umbraco.Core.Services IMember GetById(object id); + /// + /// Get a member by email + /// + /// + /// IMember GetByEmail(string email); IMember GetByUsername(string login); @@ -44,5 +49,20 @@ namespace Umbraco.Core.Services IEnumerable FindMembersByEmail(string emailStringToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); IEnumerable FindMembersByUsername(string login, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); + + /// + /// Gets the total number of members based on the count type + /// + /// + int GetMemberCount(MemberCountType countType); + + /// + /// Gets a list of paged member data + /// + /// + /// + /// + /// + IEnumerable GetAllMembers(int pageIndex, int pageSize, out int totalRecords); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberCountType.cs b/src/Umbraco.Core/Services/MemberCountType.cs new file mode 100644 index 0000000000..882d1515e2 --- /dev/null +++ b/src/Umbraco.Core/Services/MemberCountType.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Services +{ + /// + /// The types of members to count + /// + public enum MemberCountType + { + All, + Online, + LockedOut, + Approved + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index a84734d1f7..a5333cc503 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -310,8 +310,9 @@ namespace Umbraco.Core.Services /// /// /// + /// /// - public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, int value) + public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, int value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact) { using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork())) { @@ -352,8 +353,9 @@ namespace Umbraco.Core.Services /// /// /// + /// /// - public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value) + public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact) { using (var repository = _repositoryFactory.CreateMemberRepository(_uowProvider.GetUnitOfWork())) { @@ -372,6 +374,26 @@ namespace Umbraco.Core.Services #region IMembershipMemberService Implementation + /// + /// Returns the count of members based on the countType + /// + /// + /// + /// + /// The way the Online count is done is the same way that it is done in the MS SqlMembershipProvider - We query for any members + /// that have their last active date within the Membership.UserIsOnlineTimeWindow (which is in minutes). It isn't exact science + /// but that is how MS have made theirs so we'll follow that principal. + /// + public int GetMemberCount(MemberCountType countType) + { + throw new NotImplementedException(); + } + + public IEnumerable GetAllMembers(int pageIndex, int pageSize, out int totalRecords) + { + throw new NotImplementedException(); + } + /// /// Creates and persists a new Member /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 02b36c8e85..9f7bd325a1 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -203,6 +203,7 @@ + @@ -772,6 +773,7 @@ + diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs index 06e44be215..f1b8d66773 100644 --- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs +++ b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs @@ -13,6 +13,16 @@ using Umbraco.Web.Security.Providers; namespace Umbraco.Tests.Membership { + [TestFixture] + public class MembersMembershipProviderTests + { + //[Test] + //public void Set_Default_Member_Type_On_Init() + + //[Test] + //public void Question_Answer_Is_Encrypted() + } + [TestFixture] public class MembershipProviderBaseTests { @@ -111,16 +121,6 @@ namespace Umbraco.Tests.Membership private class TestProvider : MembershipProviderBase { - public override string GetPassword(string username, string answer) - { - throw new NotImplementedException(); - } - - public override string ResetPassword(string username, string answer) - { - throw new NotImplementedException(); - } - public override void UpdateUser(MembershipUser user) { throw new NotImplementedException(); @@ -190,6 +190,16 @@ namespace Umbraco.Tests.Membership { throw new NotImplementedException(); } + + protected override string PerformGetPassword(string username, string answer) + { + throw new NotImplementedException(); + } + + protected override string PerformResetPassword(string username, string answer, string generatedPassword) + { + throw new NotImplementedException(); + } } } diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index 23ee46fabc..133314d31a 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -61,48 +61,7 @@ namespace Umbraco.Web.Security.Providers // _defaultMemberTypeAlias = MemberType.GetAll[0].Alias; //else // throw new ProviderException("No default MemberType alias is specified in the web.config string. Please add a 'defaultMemberTypeAlias' to the add element in the provider declaration in web.config"); - - // test for approve status - if (config["umbracoApprovePropertyTypeAlias"] != null) - { - ApprovedPropertyTypeAlias = config["umbracoApprovePropertyTypeAlias"]; - } - // test for lock attempts - if (config["umbracoLockPropertyTypeAlias"] != null) - { - LockPropertyTypeAlias = config["umbracoLockPropertyTypeAlias"]; - } - if (config["umbracoLastLockedPropertyTypeAlias"] != null) - { - LastLockedOutPropertyTypeAlias = config["umbracoLastLockedPropertyTypeAlias"]; - } - if (config["umbracoLastPasswordChangedPropertyTypeAlias"] != null) - { - LastPasswordChangedPropertyTypeAlias = config["umbracoLastPasswordChangedPropertyTypeAlias"]; - } - if (config["umbracoFailedPasswordAttemptsPropertyTypeAlias"] != null) - { - FailedPasswordAttemptsPropertyTypeAlias = config["umbracoFailedPasswordAttemptsPropertyTypeAlias"]; - } - // comment property - if (config["umbracoCommentPropertyTypeAlias"] != null) - { - CommentPropertyTypeAlias = config["umbracoCommentPropertyTypeAlias"]; - } - // last login date - if (config["umbracoLastLoginPropertyTypeAlias"] != null) - { - LastLoginPropertyTypeAlias = config["umbracoLastLoginPropertyTypeAlias"]; - } - // password retrieval - if (config["umbracoPasswordRetrievalQuestionPropertyTypeAlias"] != null) - { - PasswordRetrievalQuestionPropertyTypeAlias = config["umbracoPasswordRetrievalQuestionPropertyTypeAlias"]; - } - if (config["umbracoPasswordRetrievalAnswerPropertyTypeAlias"] != null) - { - PasswordRetrievalAnswerPropertyTypeAlias = config["umbracoPasswordRetrievalAnswerPropertyTypeAlias"]; - } + } /// @@ -123,17 +82,7 @@ namespace Umbraco.Web.Security.Providers // in order to support updating passwords from the umbraco core, we can't validate the old password var m = _memberService.GetByUsername(username); if (m == null) return false; - - var args = new ValidatePasswordEventArgs(username, newPassword, false); - OnValidatingPassword(args); - - if (args.Cancel) - { - if (args.FailureInformation != null) - throw args.FailureInformation; - throw new MembershipPasswordException("Change password canceled due to password validation failure."); - } - + string salt; var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); @@ -289,7 +238,45 @@ namespace Umbraco.Web.Security.Providers } return collection; } - + + /// + /// Gets a collection of all the users in the data source in pages of data. + /// + /// The index of the page of results to return. pageIndex is zero-based. + /// The size of the page of results to return. + /// The total number of matched users. + /// + /// A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. + /// + public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) + { + var membersList = new MembershipUserCollection(); + + var pagedMembers = MemberService.GetAllMembers(pageIndex, pageSize, out totalRecords); + + foreach (var m in pagedMembers) + { + membersList.Add(m.AsConcreteMembershipUser()); + } + return membersList; + } + + /// + /// Gets the number of users currently accessing the application. + /// + /// + /// The number of users currently accessing the application. + /// + /// + /// The way this is done is the same way that it is done in the MS SqlMembershipProvider - We query for any members + /// that have their last active date within the Membership.UserIsOnlineTimeWindow (which is in minutes). It isn't exact science + /// but that is how MS have made theirs so we'll follow that principal. + /// + public override int GetNumberOfUsersOnline() + { + return MemberService.GetMemberCount(MemberCountType.Online); + } + /// /// Gets the password for the specified user name from the data source. /// @@ -298,168 +285,24 @@ namespace Umbraco.Web.Security.Providers /// /// The password for the specified user name. /// - public override string GetPassword(string username, string answer) - { - if (EnablePasswordRetrieval == false) - throw new ProviderException("Password Retrieval Not Enabled."); - - if (PasswordFormat == MembershipPasswordFormat.Hashed) - throw new ProviderException("Cannot retrieve Hashed passwords."); - - var member = MemberService.GetByUsername(username); - - if (RequiresQuestionAndAnswer && member.PasswordAnswer != answer) + protected override string PerformGetPassword(string username, string answer) + { + var m = MemberService.GetByUsername(username); + if (m == null) { - throw new ProviderException("Password retrieval answer doesn't match"); - } - - return member.Password; - } - - /// - /// Resets a user's password to a new, automatically generated password. - /// - /// The user to reset the password for. - /// The password answer for the specified user (not used with Umbraco). - /// The new password for the specified user. - public override string ResetPassword(string username, string answer) - { - //TODO: Get logic from other provider - - throw new NotImplementedException(); - - //if (EnablePasswordReset == false) - // throw new ProviderException("Password reset is Not Enabled."); - - //var member = MemberService.GetByUsername(username); - - //if (member == null) - // throw new ProviderException("The supplied user is not found"); - - //if(member.IsLockedOut) - // throw new ProviderException("The member is locked out."); - - //if (RequiresQuestionAndAnswer == false || (RequiresQuestionAndAnswer && answer == member.PasswordAnswer)) - //{ - // member.Password = - // EncryptOrHashPassword(Membership.GeneratePassword(MinRequiredPasswordLength, - // MinRequiredNonAlphanumericCharacters)); - // MemberService.Save(member); - //} - //else - //{ - // throw new MembershipPasswordException("Incorrect password answer"); - //} - - //return null; - } - - /// - /// Updates e-mail and potentially approved status, lock status and comment on a user. - /// Note: To automatically support lock, approve and comments you'll need to add references to the membertype properties in the - /// 'Member' element in web.config by adding their aliases to the 'umbracoApprovePropertyTypeAlias', 'umbracoLockPropertyTypeAlias' and 'umbracoCommentPropertyTypeAlias' attributes - /// - /// A object that represents the user to update and the updated information for the user. - public override void UpdateUser(MembershipUser user) - { - //var member = user.AsIMember(); - //MemberService.Save(member); - } - - /// - /// Verifies that the specified user name and password exist in the data source. - /// - /// The name of the user to validate. - /// The password for the specified user. - /// - /// true if the specified username and password are valid; otherwise, false. - /// - public override bool ValidateUser(string username, string password) - { - var member = MemberService.GetByUsername(username); - - if (member == null || member.IsApproved == false) return false; - - if (member.IsLockedOut) - throw new ProviderException("The member is locked out."); - - string salt; - var encodedPassword = EncryptOrHashNewPassword(password, out salt); - - var authenticated = (encodedPassword == member.Password); - - if (authenticated == false) - { - // TODO: Increment login attempts - lock if too many. - - //var count = member.GetValue("loginAttempts"); - //count++; - - //if (count >= _maxInvalidPasswordAttempts) - //{ - // member.SetValue("loginAttempts", 0); - // member.IsLockedOut = true; - // throw new ProviderException("The member " + member.Username + " is locked out."); - //} - //else - //{ - // member.SetValue("loginAttempts", count); - //} - } - else - { - // add this later - member.SetValue("loginAttempts", 0); - member.LastLoginDate = DateTime.Now; + throw new ProviderException("The supplied user is not found"); } - MemberService.Save(member); - return authenticated; - } + //TODO: We need to encrypt the answer here to match against the encrypted answer in the database - /// - /// Clears a lock so that the membership user can be validated. - /// - /// The membership user to clear the lock status for. - /// - /// true if the membership user was successfully unlocked; otherwise, false. - /// - public override bool UnlockUser(string username) - { - var member = MemberService.GetByUsername(username); - - if(member == null) - throw new ProviderException("Cannot find member " + username); - - // Non need to update - if (member.IsLockedOut == false) return true; - - member.IsLockedOut = false; - // TODO: add this later - member.SetValue("loginAttempts", 0); - - MemberService.Save(member); - - return true; - } - - /// - /// Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. - /// - /// The unique identifier for the membership user to get information for. - /// true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. - /// - /// A object populated with the specified user's information from the data source. - /// - public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) - { - var member = MemberService.GetById(providerUserKey); - - if (userIsOnline) + if (RequiresQuestionAndAnswer && m.PasswordAnswer != answer) { - member.UpdateDate = DateTime.Now; - MemberService.Save(member); + throw new ProviderException("Incorrect password answer"); } - return member.AsConcreteMembershipUser(); + var decodedPassword = DecodePassword(m.Password); + + return decodedPassword; } /// @@ -473,9 +316,40 @@ namespace Umbraco.Web.Security.Providers public override MembershipUser GetUser(string username, bool userIsOnline) { var member = MemberService.GetByUsername(username); + if (member == null) + { + return null; + } if (userIsOnline) { + member.LastLoginDate = DateTime.Now; + member.UpdateDate = DateTime.Now; + MemberService.Save(member); + } + + return member.AsConcreteMembershipUser(); + } + + /// + /// Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// The unique identifier for the membership user to get information for. + /// true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. + /// + /// A object populated with the specified user's information from the data source. + /// + public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) + { + var member = MemberService.GetById(providerUserKey); + if (member == null) + { + return null; + } + + if (userIsOnline) + { + member.LastLoginDate = DateTime.Now; member.UpdateDate = DateTime.Now; MemberService.Save(member); } @@ -497,33 +371,158 @@ namespace Umbraco.Web.Security.Providers return member == null ? null : member.Username; } - - /// - /// Gets a collection of all the users in the data source in pages of data. + /// Resets a user's password to a new, automatically generated password. /// - /// The index of the page of results to return. pageIndex is zero-based. - /// The size of the page of results to return. - /// The total number of matched users. - /// - /// A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. - /// - public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) + /// The user to reset the password for. + /// The password answer for the specified user (not used with Umbraco). + /// + /// The new password for the specified user. + protected override string PerformResetPassword(string username, string answer, string generatedPassword) { - throw new System.NotImplementedException(); + //TODO: This should be here - but how do we update failure count in this provider?? + //if (answer == null && RequiresQuestionAndAnswer) + //{ + // UpdateFailureCount(username, "passwordAnswer"); + + // throw new ProviderException("Password answer required for password reset."); + //} + + var m = MemberService.GetByUsername(username); + if (m == null) + { + throw new ProviderException("The supplied user is not found"); + } + + if (m.IsLockedOut) + { + throw new ProviderException("The member is locked out."); + } + + //TODO: We need to encrypt the answer here to match against the encrypted answer in the database + + if (RequiresQuestionAndAnswer && m.PasswordAnswer != answer) + { + throw new ProviderException("Incorrect password answer"); + } + + string salt; + var encodedPassword = EncryptOrHashNewPassword(generatedPassword, out salt); + m.Password = encodedPassword; + m.LastPasswordChangeDate = DateTime.Now; + MemberService.Save(m); + + return generatedPassword; } /// - /// Gets the number of users currently accessing the application. + /// Clears a lock so that the membership user can be validated. /// + /// The membership user to clear the lock status for. /// - /// The number of users currently accessing the application. + /// true if the membership user was successfully unlocked; otherwise, false. /// - public override int GetNumberOfUsersOnline() + public override bool UnlockUser(string username) { - throw new System.NotImplementedException(); + var member = MemberService.GetByUsername(username); + + if (member == null) + { + throw new ProviderException(string.Format("No member with the username '{0}' found", username)); + } + + // Non need to update + if (member.IsLockedOut == false) return true; + + member.IsLockedOut = false; + member.FailedPasswordAttempts = 0; + + MemberService.Save(member); + + return true; } - + + /// + /// Updates e-mail approved status, lock status and comment on a user. + /// + /// A object that represents the user to update and the updated information for the user. + public override void UpdateUser(MembershipUser user) + { + var m = MemberService.GetByUsername(user.UserName); + + if (m == null) + { + throw new ProviderException(string.Format("No member with the username '{0}' found", user.UserName)); + } + + m.Email = user.Email; + m.IsApproved = user.IsApproved; + m.IsLockedOut = user.IsLockedOut; + if (user.IsLockedOut) + { + m.LastLockoutDate = DateTime.Now; + } + m.Comments = user.Comment; + + MemberService.Save(m); + } + + /// + /// Verifies that the specified user name and password exist in the data source. + /// + /// The name of the user to validate. + /// The password for the specified user. + /// + /// true if the specified username and password are valid; otherwise, false. + /// + public override bool ValidateUser(string username, string password) + { + var member = MemberService.GetByUsername(username); + + if (member == null) return false; + + if (member.IsApproved == false) + { + LogHelper.Info("Cannot validate member " + username + " because they are not approved"); + return false; + } + if (member.IsLockedOut) + { + LogHelper.Info("Cannot validate member " + username + " because they are currently locked out"); + return false; + } + + string salt; + var encodedPassword = EncryptOrHashNewPassword(password, out salt); + + var authenticated = (encodedPassword == member.Password); + + if (authenticated == false) + { + // TODO: Increment login attempts - lock if too many. + + var count = member.FailedPasswordAttempts; + count++; + member.FailedPasswordAttempts = count; + + if (count >= MaxInvalidPasswordAttempts) + { + member.IsLockedOut = true; + member.LastLockoutDate = DateTime.Now; + LogHelper.Info("Member " + username + " is now locked out, max invalid password attempts exceeded"); + } + } + else + { + member.FailedPasswordAttempts = 0; + member.LastLoginDate = DateTime.Now; + } + + MemberService.Save(member); + return authenticated; + } + + public override string ToString() { diff --git a/src/umbraco.providers/UsersMembershipProvider.cs b/src/umbraco.providers/UsersMembershipProvider.cs index e862bf5b19..64534abf3a 100644 --- a/src/umbraco.providers/UsersMembershipProvider.cs +++ b/src/umbraco.providers/UsersMembershipProvider.cs @@ -305,7 +305,7 @@ namespace umbraco.providers /// /// The password for the specified user name. /// - public override string GetPassword(string username, string answer) + protected override string PerformGetPassword(string username, string answer) { throw new ProviderException("Password Retrieval Not Enabled."); } @@ -355,13 +355,8 @@ namespace umbraco.providers /// The user to reset the password for. /// The password answer for the specified user. /// The new password for the specified user. - public override string ResetPassword(string username, string answer) - { - if (EnablePasswordReset == false) - { - throw new NotSupportedException("Password reset is not supported"); - } - + protected override string PerformResetPassword(string username, string answer, string generatedPassword) + { //TODO: This should be here - but how do we update failure count in this provider?? //if (answer == null && RequiresQuestionAndAnswer) //{ @@ -370,18 +365,7 @@ namespace umbraco.providers // throw new ProviderException("Password answer required for password reset."); //} - var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); - - var args = new ValidatePasswordEventArgs(username, newPassword, true); - OnValidatingPassword(args); - if (args.Cancel) - { - if (args.FailureInformation != null) - throw args.FailureInformation; - throw new MembershipPasswordException("Reset password canceled due to password validation failure."); - } - - var found = User.GetAllByLoginName(username, false); + var found = User.GetAllByLoginName(username, false).ToArray(); if (found == null || found.Any() == false) throw new MembershipPasswordException("The supplied user is not found"); @@ -389,12 +373,12 @@ namespace umbraco.providers //Yes, it's true, this actually makes a db call to set the password string salt; - var encPass = EncryptOrHashNewPassword(newPassword, out salt); + var encPass = EncryptOrHashNewPassword(generatedPassword, out salt); user.Password = FormatPasswordForStorage(encPass, salt); //call this just for fun. user.Save(); - return newPassword; + return generatedPassword; } /// diff --git a/src/umbraco.providers/members/UmbracoMembershipProvider.cs b/src/umbraco.providers/members/UmbracoMembershipProvider.cs index 62562a85b0..500444b3fd 100644 --- a/src/umbraco.providers/members/UmbracoMembershipProvider.cs +++ b/src/umbraco.providers/members/UmbracoMembershipProvider.cs @@ -31,12 +31,35 @@ namespace umbraco.providers.members public class UmbracoMembershipProvider : UmbracoMembershipProviderBase { + public UmbracoMembershipProvider() + { + LockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut; + LastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate; + FailedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts; + ApprovedPropertyTypeAlias = Constants.Conventions.Member.IsApproved; + CommentPropertyTypeAlias = Constants.Conventions.Member.Comments; + LastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate; + LastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate; + PasswordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion; + PasswordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer; + } + #region Fields private string _providerName = Member.UmbracoMemberProviderName; #endregion - + + public string LockPropertyTypeAlias { get; protected set; } + public string LastLockedOutPropertyTypeAlias { get; protected set; } + public string FailedPasswordAttemptsPropertyTypeAlias { get; protected set; } + public string ApprovedPropertyTypeAlias { get; protected set; } + public string CommentPropertyTypeAlias { get; protected set; } + public string LastLoginPropertyTypeAlias { get; protected set; } + public string LastPasswordChangedPropertyTypeAlias { get; protected set; } + public string PasswordRetrievalQuestionPropertyTypeAlias { get; protected set; } + public string PasswordRetrievalAnswerPropertyTypeAlias { get; protected set; } + /// /// Override to maintain backwards compatibility with 0 required non-alphanumeric chars /// @@ -164,17 +187,7 @@ namespace umbraco.providers.members // in order to support updating passwords from the umbraco core, we can't validate the old password var m = Member.GetMemberFromLoginName(username); if (m == null) return false; - - var args = new ValidatePasswordEventArgs(username, newPassword, false); - OnValidatingPassword(args); - - if (args.Cancel) - { - if (args.FailureInformation != null) - throw args.FailureInformation; - throw new MembershipPasswordException("Change password canceled due to password validation failure."); - } - + string salt; var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); m.ChangePassword( @@ -199,6 +212,11 @@ namespace umbraco.providers.members /// protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { + if (string.IsNullOrEmpty(PasswordRetrievalQuestionPropertyTypeAlias) || string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias)) + { + throw new NotSupportedException("Updating the password Question and Answer is not valid if the properties aren't set in the config file"); + } + var m = Member.GetMemberFromLoginName(username); if (m == null) { @@ -409,50 +427,45 @@ namespace umbraco.providers.members /// /// The password for the specified user name. /// - public override string GetPassword(string username, string answer) + protected override string PerformGetPassword(string username, string answer) { - if (EnablePasswordRetrieval == false) - throw new ProviderException("Password Retrieval Not Enabled."); - - if (PasswordFormat == MembershipPasswordFormat.Hashed) - throw new ProviderException("Cannot retrieve Hashed passwords."); - var m = Member.GetMemberFromLoginName(username); - if (m != null) - { - if (RequiresQuestionAndAnswer) - { - // check if password answer property alias is set - if (string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias) == false) - { - // check if user is locked out - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - var isLockedOut = false; - bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); - if (isLockedOut) - { - throw new MembershipPasswordException("The supplied user is locked out"); - } - } - - // match password answer - if (GetMemberProperty(m, PasswordRetrievalAnswerPropertyTypeAlias, false) != answer) - { - throw new MembershipPasswordException("Incorrect password answer"); - } - } - else - { - throw new ProviderException("Password retrieval answer property alias is not set! To automatically support password question/answers you'll need to add references to the membertype properties in the 'Member' element in web.config by adding their aliases to the 'umbracoPasswordRetrievalQuestionPropertyTypeAlias' and 'umbracoPasswordRetrievalAnswerPropertyTypeAlias' attributes"); - } - } - } if (m == null) { throw new MembershipPasswordException("The supplied user is not found"); } - return m.GetPassword(); + + // check if user is locked out + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + var isLockedOut = false; + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); + if (isLockedOut) + { + throw new MembershipPasswordException("The supplied user is locked out"); + } + } + + if (RequiresQuestionAndAnswer) + { + // check if password answer property alias is set + if (string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias) == false) + { + // match password answer + if (GetMemberProperty(m, PasswordRetrievalAnswerPropertyTypeAlias, false) != answer) + { + throw new MembershipPasswordException("Incorrect password answer"); + } + } + else + { + throw new ProviderException("Password retrieval answer property alias is not set! To automatically support password question/answers you'll need to add references to the membertype properties in the 'Member' element in web.config by adding their aliases to the 'umbracoPasswordRetrievalQuestionPropertyTypeAlias' and 'umbracoPasswordRetrievalAnswerPropertyTypeAlias' attributes"); + } + } + + var decodedPassword = DecodePassword(m.GetPassword()); + + return decodedPassword; } /// @@ -465,11 +478,21 @@ namespace umbraco.providers.members /// public override MembershipUser GetUser(string username, bool userIsOnline) { - if (String.IsNullOrEmpty(username)) + if (string.IsNullOrEmpty(username)) return null; - Member m = Member.GetMemberFromLoginName(username); - if (m == null) return null; - else return ConvertToMembershipUser(m); + var m = Member.GetMemberFromLoginName(username); + + if (m == null) + { + return null; + } + + if (userIsOnline && LastLoginPropertyTypeAlias.IsNullOrWhiteSpace() == false) + { + UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); + } + + return ConvertToMembershipUser(m); } /// @@ -486,14 +509,23 @@ namespace umbraco.providers.members if (asGuid.Success) { var m = new Member(asGuid.Result); + if (userIsOnline && LastLoginPropertyTypeAlias.IsNullOrWhiteSpace() == false) + { + UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); + } return ConvertToMembershipUser(m); } var asInt = providerUserKey.TryConvertTo(); if (asInt.Success) { var m = new Member(asInt.Result); + if (userIsOnline && LastLoginPropertyTypeAlias.IsNullOrWhiteSpace() == false) + { + UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); + } return ConvertToMembershipUser(m); } + throw new InvalidOperationException("The " + GetType() + " provider only supports GUID or Int as a providerUserKey"); } @@ -508,7 +540,7 @@ namespace umbraco.providers.members /// public override string GetUserNameByEmail(string email) { - Member m = Member.GetMemberFromEmail(email); + var m = Member.GetMemberFromEmail(email); return m == null ? null : m.LoginName; } @@ -517,14 +549,10 @@ namespace umbraco.providers.members /// /// The user to reset the password for. /// The password answer for the specified user (not used with Umbraco). + /// /// The new password for the specified user. - public override string ResetPassword(string username, string answer) + protected override string PerformResetPassword(string username, string answer, string generatedPassword) { - if (EnablePasswordReset == false) - { - throw new NotSupportedException("Password reset is not supported"); - } - //TODO: This should be here - but how do we update failure count in this provider?? //if (answer == null && RequiresQuestionAndAnswer) //{ @@ -532,42 +560,33 @@ namespace umbraco.providers.members // throw new ProviderException("Password answer required for password reset."); //} - - var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters); - - var args = new ValidatePasswordEventArgs(username, newPassword, true); - OnValidatingPassword(args); - if (args.Cancel) - { - if (args.FailureInformation != null) - throw args.FailureInformation; - throw new MembershipPasswordException("Reset password canceled due to password validation failure."); - } var m = Member.GetMemberFromLoginName(username); if (m == null) - throw new MembershipPasswordException("The supplied user is not found"); + { + throw new ProviderException("The supplied user is not found"); + } + + // check if user is locked out + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + var isLockedOut = false; + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); + if (isLockedOut) + { + throw new ProviderException("The member is locked out."); + } + } if (RequiresQuestionAndAnswer) { // check if password answer property alias is set if (string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias) == false) { - // check if user is locked out - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - var isLockedOut = false; - bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLockedOut); - if (isLockedOut) - { - throw new MembershipPasswordException("The supplied user is locked out"); - } - } - // match password answer if (GetMemberProperty(m, PasswordRetrievalAnswerPropertyTypeAlias, false) != answer) { - throw new MembershipPasswordException("Incorrect password answer"); + throw new ProviderException("Incorrect password answer"); } } else @@ -577,10 +596,9 @@ namespace umbraco.providers.members } string salt; - var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); + var encodedPassword = EncryptOrHashNewPassword(generatedPassword, out salt); //set the password on the member - m.ChangePassword( - FormatPasswordForStorage(encodedPassword, salt)); + m.ChangePassword(FormatPasswordForStorage(encodedPassword, salt)); if (string.IsNullOrEmpty(LastPasswordChangedPropertyTypeAlias) == false) { @@ -589,7 +607,7 @@ namespace umbraco.providers.members m.Save(); - return newPassword; + return generatedPassword; } /// @@ -607,10 +625,14 @@ namespace umbraco.providers.members if (m != null) { UpdateMemberProperty(m, LockPropertyTypeAlias, 0); + if (string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, 0); + } m.Save(); return true; } - throw new Exception(String.Format("No member with the username '{0}' found", userName)); + throw new ProviderException(string.Format("No member with the username '{0}' found", userName)); } throw new ProviderException("To enable lock/unlocking, you need to add a 'bool' property on your membertype and add the alias of the property in the 'umbracoLockPropertyTypeAlias' attribute of the membership element in the web.config."); } @@ -622,6 +644,12 @@ namespace umbraco.providers.members public override void UpdateUser(MembershipUser user) { var m = Member.GetMemberFromLoginName(user.UserName); + + if (m == null) + { + throw new ProviderException(string.Format("No member with the username '{0}' found", user.UserName)); + } + m.Email = user.Email; // if supported, update approve status @@ -640,6 +668,86 @@ namespace umbraco.providers.members m.Save(); } + /// + /// Verifies that the specified user name and password exist in the data source. + /// + /// The name of the user to validate. + /// The password for the specified user. + /// + /// true if the specified username and password are valid; otherwise, false. + /// + public override bool ValidateUser(string username, string password) + { + var m = Member.GetMemberFromLoginAndEncodedPassword(username, EncryptOrHashExistingPassword(password)); + if (m != null) + { + // check for lock status. If locked, then set the member property to null + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + string lockedStatus = GetMemberProperty(m, LockPropertyTypeAlias, true); + if (string.IsNullOrEmpty(lockedStatus) == false) + { + var isLocked = false; + if (bool.TryParse(lockedStatus, out isLocked)) + { + if (isLocked) + { + LogHelper.Info("Cannot validate member " + username + " because they are currently locked out"); + m = null; + } + } + } + } + + //check for approve status. If not approved, then set the member property to null + if (m != null && CheckApproveStatus(m) == false) + { + LogHelper.Info("Cannot validate member " + username + " because they are not approved"); + m = null; + } + + // maybe update login date + if (m != null && string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); + } + + // maybe reset password attempts + if (m != null && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, 0); + } + + // persist data + if (m != null) + m.Save(); + } + else if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false + && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) + { + var updateMemberDataObject = Member.GetMemberFromLoginName(username); + // update fail rate if it's approved + if (updateMemberDataObject != null && CheckApproveStatus(updateMemberDataObject)) + { + int failedAttempts = 0; + int.TryParse(GetMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); + failedAttempts = failedAttempts + 1; + UpdateMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); + + // lock user? + if (failedAttempts >= MaxInvalidPasswordAttempts) + { + UpdateMemberProperty(updateMemberDataObject, LockPropertyTypeAlias, 1); + UpdateMemberProperty(updateMemberDataObject, LastLockedOutPropertyTypeAlias, DateTime.Now); + LogHelper.Info("Member " + username + " is now locked out, max invalid password attempts exceeded"); + } + updateMemberDataObject.Save(); + } + + } + return (m != null); + } + private static void UpdateMemberProperty(Member m, string propertyTypeAlias, object propertyValue) { if (string.IsNullOrEmpty(propertyTypeAlias) == false) @@ -688,83 +796,7 @@ namespace umbraco.providers.members return null; } - - /// - /// Verifies that the specified user name and password exist in the data source. - /// - /// The name of the user to validate. - /// The password for the specified user. - /// - /// true if the specified username and password are valid; otherwise, false. - /// - public override bool ValidateUser(string username, string password) - { - var m = Member.GetMemberFromLoginAndEncodedPassword(username, EncryptOrHashExistingPassword(password)); - if (m != null) - { - // check for lock status. If locked, then set the member property to null - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - string lockedStatus = GetMemberProperty(m, LockPropertyTypeAlias, true); - if (string.IsNullOrEmpty(lockedStatus) == false) - { - var isLocked = false; - if (bool.TryParse(lockedStatus, out isLocked)) - { - if (isLocked) - { - m = null; - } - } - } - } - - //check for approve status. If not approved, then set the member property to null - if (m != null && !CheckApproveStatus(m)) { - m = null; - } - - // maybe update login date - if (m != null && string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); - } - - // maybe reset password attempts - if (m != null && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, 0); - } - - // persist data - if (m != null) - m.Save(); - } - else if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false - && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) - { - var updateMemberDataObject = Member.GetMemberFromLoginName(username); - // update fail rate if it's approved - if (updateMemberDataObject != null && CheckApproveStatus(updateMemberDataObject)) - { - int failedAttempts = 0; - int.TryParse(GetMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); - failedAttempts = failedAttempts+1; - UpdateMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); - - // lock user? - if (failedAttempts >= MaxInvalidPasswordAttempts) - { - UpdateMemberProperty(updateMemberDataObject, LockPropertyTypeAlias, 1); - UpdateMemberProperty(updateMemberDataObject, LastLockedOutPropertyTypeAlias, DateTime.Now); - } - updateMemberDataObject.Save(); - } - - } - return (m != null); - } - + private bool CheckApproveStatus(Member m) { var isApproved = false; @@ -834,7 +866,7 @@ namespace umbraco.providers.members /// /// The password. /// The encoded password. - [Obsolete("Do not use this, it is the legacy way to encode a password")] + [Obsolete("Do not use this, it is the legacy way to encode a password - use the base class EncryptOrHashExistingPassword instead")] public string EncodePassword(string password) { return LegacyEncodePassword(password); @@ -845,7 +877,7 @@ namespace umbraco.providers.members /// /// The encoded password. /// The unencoded password. - [Obsolete("Do not use this, it is the legacy way to decode a password")] + [Obsolete("Do not use this, it is the legacy way to decode a password - use the base class DecodePassword instead")] public string UnEncodePassword(string encodedPassword) { return LegacyUnEncodePassword(encodedPassword);