From 2dd04799b216651314e780776e88e547290c7b8f Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Dec 2013 18:33:25 +1100 Subject: [PATCH] WIP membership provider updates --- .../Models/Membership/MembershipExtensions.cs | 6 +- .../Querying/ModelToSqlExpressionHelper.cs | 4 +- .../Querying/SqlStringExtensions.cs | 10 + .../Querying/StringPropertyMatchType.cs | 4 +- .../SqlSyntax/ISqlSyntaxProvider.cs | 1 + .../SqlSyntax/SqlCeSyntaxProvider.cs | 14 + .../SqlSyntax/SqlServerSyntaxProvider.cs | 14 + .../SqlSyntax/SqlSyntaxProviderBase.cs | 6 + .../Security/MembershipProviderBase.cs | 312 ++- src/Umbraco.Core/Services/MemberService.cs | 6 + .../Membership/MembershipProviderBaseTests.cs | 127 ++ .../Providers/MembersMembershipProvider.cs | 745 ++---- .../UsersMembershipProvider.cs | 10 +- ...ovider.cs => UmbracoMembershipProvider.cs} | 1989 ++++++++--------- ...eProvider.cs => UmbracoProfileProvider.cs} | 346 +-- ...RoleProvider.cs => UmbracoRoleProvider.cs} | 509 ++--- .../umbraco.providers.csproj | 6 +- 17 files changed, 2094 insertions(+), 2015 deletions(-) rename src/umbraco.providers/members/{MembersMembershipProvider.cs => UmbracoMembershipProvider.cs} (71%) rename src/umbraco.providers/members/{MembersProfileProvider.cs => UmbracoProfileProvider.cs} (69%) rename src/umbraco.providers/members/{MembersRoleProvider.cs => UmbracoRoleProvider.cs} (97%) diff --git a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs b/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs index a986869371..118ba46669 100644 --- a/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/MembershipExtensions.cs @@ -5,15 +5,15 @@ namespace Umbraco.Core.Models.Membership { internal static class MembershipExtensions { - internal static MembershipUser AsConcreteMembershipUser(this IMember member) + internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMember member) { var membershipMember = new UmbracoMembershipMember(member); return membershipMember; } - internal static IMember AsIMember(this MembershipUser membershipMember) + internal static IMember AsIMember(this UmbracoMembershipMember membershipMember) { - var member = membershipMember as UmbracoMembershipMember; + var member = membershipMember; if (member != null) { return member.Member; diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 611d9c63a3..4dba620ab0 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -232,6 +232,8 @@ namespace Umbraco.Core.Persistence.Querying { switch (verb) { + case "SqlWildcard": + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType); case "Equals": return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType); case "StartsWith": @@ -280,7 +282,7 @@ namespace Umbraco.Core.Persistence.Querying return string.Format("upper({0})", r); case "ToLower": return string.Format("lower({0})", r); - + case "SqlWildcard": case "StartsWith": case "EndsWith": case "Contains": diff --git a/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs index 82c158a9c6..cbecc0a591 100644 --- a/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs +++ b/src/Umbraco.Core/Persistence/Querying/SqlStringExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Text.RegularExpressions; namespace Umbraco.Core.Persistence.Querying { @@ -7,6 +8,15 @@ namespace Umbraco.Core.Persistence.Querying /// internal static class SqlStringExtensions { + public static bool SqlWildcard(this string str, string txt, TextColumnType columnType) + { + var wildcardmatch = new Regex("^" + Regex.Escape(txt). + //deal with any wildcard chars % + Replace(@"\%", ".*") + "$"); + + return wildcardmatch.IsMatch(str); + } + public static bool SqlContains(this string str, string txt, TextColumnType columnType) { return str.InvariantContains(txt); diff --git a/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs index f4245b931a..f45efb412a 100644 --- a/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs +++ b/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs @@ -8,6 +8,8 @@ Exact, Contains, StartsWith, - EndsWith + EndsWith, + //Deals with % as wildcard chars in a string + Wildcard } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 9fbf0456b6..2b31fb2032 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType); string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType); string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType); + string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType); string GetQuotedTableName(string tableName); string GetQuotedColumnName(string columnName); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index d908f5c9b6..d9e9599dab 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -116,6 +116,20 @@ namespace Umbraco.Core.Persistence.SqlSyntax } } + public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + public override string GetQuotedTableName(string tableName) { return string.Format("[{0}]", tableName); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 24177c61d5..4046a7575d 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -90,6 +90,20 @@ namespace Umbraco.Core.Persistence.SqlSyntax } } + public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + public override string GetQuotedTableName(string tableName) { return string.Format("[{0}]", tableName); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 8505d1e553..8f9a84437c 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -127,6 +127,12 @@ namespace Umbraco.Core.Persistence.SqlSyntax return string.Format("upper({0}) like '%{1}%'", column, value.ToUpper()); } + public virtual string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like '{1}'", column, value.ToUpper()); + } + public virtual string GetQuotedTableName(string tableName) { return string.Format("\"{0}\"", tableName); diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index 10a05d042e..24a0e29409 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -7,9 +7,110 @@ using System.Text.RegularExpressions; using System.Web.Configuration; using System.Web.Hosting; using System.Web.Security; +using Umbraco.Core.Logging; namespace Umbraco.Core.Security { + + public abstract class UmbracoMembershipProviderBase : MembershipProviderBase + { + protected UmbracoMembershipProviderBase() + { + //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. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected sealed override MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + return PerformCreateUser(DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Adds a new membership user to the data source. + /// + /// The member type alias to use when creating the member + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + //do the base validation first + base.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + + return PerformCreateUser(memberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Adds a new membership user to the data source. + /// + /// The member type alias to use when creating the member + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected abstract MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); + } /// /// A base membership provider class offering much of the underlying functionality for initializing and password encryption/hashing. /// @@ -18,7 +119,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is 7 /// - protected virtual int DefaultMinPasswordLength + public virtual int DefaultMinPasswordLength { get { return 7; } } @@ -26,7 +127,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is 1 /// - protected virtual int DefaultMinNonAlphanumericChars + public virtual int DefaultMinNonAlphanumericChars { get { return 1; } } @@ -34,7 +135,7 @@ namespace Umbraco.Core.Security /// /// Providers can override this setting, default is false to use better security /// - protected virtual bool DefaultUseLegacyEncoding + public virtual bool DefaultUseLegacyEncoding { get { return false; } } @@ -44,7 +145,7 @@ namespace Umbraco.Core.Security /// authenticate the username + password when ChangePassword is called. This property exists purely for /// backwards compatibility. /// - internal virtual bool AllowManuallyChangingPassword + public virtual bool AllowManuallyChangingPassword { get { return false; } } @@ -60,7 +161,8 @@ namespace Umbraco.Core.Security private string _passwordStrengthRegularExpression; private bool _requiresQuestionAndAnswer; private bool _requiresUniqueEmail; - private bool _useLegacyEncoding; + + internal bool UseLegacyEncoding; #region Properties @@ -208,7 +310,7 @@ namespace Umbraco.Core.Security _enablePasswordRetrieval = config.GetValue("enablePasswordRetrieval", false); _enablePasswordReset = config.GetValue("enablePasswordReset", false); _requiresQuestionAndAnswer = config.GetValue("requiresQuestionAndAnswer", false); - _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", false); + _requiresUniqueEmail = config.GetValue("requiresUniqueEmail", true); _maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); _passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0); _minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80); @@ -220,10 +322,10 @@ namespace Umbraco.Core.Security _applicationName = GetDefaultAppName(); //by default we will continue using the legacy encoding. - _useLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); + UseLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding); - // make sure password format is clear by default. - string str = config["passwordFormat"] ?? "Clear"; + // make sure password format is Hashed by default. + string str = config["passwordFormat"] ?? "Hashed"; switch (str.ToLower()) { @@ -244,7 +346,11 @@ namespace Umbraco.Core.Security } if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) - throw new ProviderException("Provider can not retrieve hashed password"); + { + var ex = new ProviderException("Provider can not retrieve a hashed password"); + LogHelper.Error("Cannot specify a Hashed password format with the enabledPasswordRetrieval option set to true", ex); + throw ex; + } } @@ -271,14 +377,19 @@ namespace Umbraco.Core.Security AlphanumericChars, Strength } - + /// - /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to + /// Processes a request to update the password for a membership user. /// - /// - /// - /// - /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// + /// + /// Checks to ensure the AllowManuallyChangingPassword rule is adhered to + /// public sealed override bool ChangePassword(string username, string oldPassword, string newPassword) { if (oldPassword.IsNullOrWhiteSpace() && AllowManuallyChangingPassword == false) @@ -290,8 +401,139 @@ namespace Umbraco.Core.Security return PerformChangePassword(username, oldPassword, newPassword); } + /// + /// Processes a request to update the password for a membership user. + /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// protected abstract bool PerformChangePassword(string username, string oldPassword, string newPassword); + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + /// + /// Performs the basic validation before passing off to PerformChangePasswordQuestionAndAnswer + /// + public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + if (RequiresQuestionAndAnswer == false) + { + 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) + { + return false; + } + + return PerformChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); + } + + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + protected abstract bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer); + + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + /// + /// Ensures the ValidatingPassword event is executed before executing PerformCreateUser and performs basic membership provider validation of values. + /// + public sealed override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + var args = new ValidatePasswordEventArgs(username, password, true); + OnValidatingPassword(args); + if (args.Cancel) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + // Validate password + var passwordValidAttempt = IsPasswordValid(password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength); + if (passwordValidAttempt.Success == false) + { + status = MembershipCreateStatus.InvalidPassword; + return null; + } + + // Validate email + if (IsEmailValid(email) == false) + { + status = MembershipCreateStatus.InvalidEmail; + return null; + } + + // Make sure username isn't all whitespace + if (string.IsNullOrWhiteSpace(username.Trim())) + { + status = MembershipCreateStatus.InvalidUserName; + return null; + } + + // Check password question + if (string.IsNullOrWhiteSpace(passwordQuestion) && RequiresQuestionAndAnswer) + { + status = MembershipCreateStatus.InvalidQuestion; + return null; + } + + // Check password answer + if (string.IsNullOrWhiteSpace(passwordAnswer) && RequiresQuestionAndAnswer) + { + status = MembershipCreateStatus.InvalidAnswer; + return null; + } + + return PerformCreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + } + + /// + /// Adds a new membership user to the data source. + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected abstract MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); + protected internal static Attempt IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength) { if (minRequiredNonAlphanumericChars > 0) @@ -374,7 +616,7 @@ namespace Umbraco.Core.Security protected string FormatPasswordForStorage(string pass, string salt) { - if (_useLegacyEncoding) + if (UseLegacyEncoding) { return pass; } @@ -383,11 +625,20 @@ namespace Umbraco.Core.Security return salt + pass; } + protected bool IsEmailValid(string email) + { + const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" + + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? protected string EncryptOrHashExistingPassword(string storedPassword) { - if (_useLegacyEncoding) + if (UseLegacyEncoding) { return EncryptOrHashPassword(storedPassword, storedPassword); } @@ -481,7 +732,7 @@ namespace Umbraco.Core.Security { //if we are doing it the old way - if (_useLegacyEncoding) + if (UseLegacyEncoding) { return LegacyUnEncodePassword(pass); } @@ -533,7 +784,7 @@ namespace Umbraco.Core.Security protected HashAlgorithm GetHashAlgorithm(string password) { - if (_useLegacyEncoding) + if (UseLegacyEncoding) { //before we were never checking for an algorithm type so we were always using HMACSHA1 // for any SHA specified algorithm :( so we'll need to keep doing that for backwards compat support. @@ -601,5 +852,24 @@ namespace Umbraco.Core.Security return password; } + public override string ToString() + { + var result = base.ToString(); + var sb = new StringBuilder(result); + sb.AppendLine("Name =" + Name); + sb.AppendLine("_applicationName =" + _applicationName); + sb.AppendLine("_enablePasswordReset=" + _enablePasswordReset); + sb.AppendLine("_enablePasswordRetrieval=" + _enablePasswordRetrieval); + sb.AppendLine("_maxInvalidPasswordAttempts=" + _maxInvalidPasswordAttempts); + sb.AppendLine("_minRequiredNonAlphanumericCharacters=" + _minRequiredNonAlphanumericCharacters); + sb.AppendLine("_minRequiredPasswordLength=" + _minRequiredPasswordLength); + sb.AppendLine("_passwordAttemptWindow=" + _passwordAttemptWindow); + sb.AppendLine("_passwordFormat=" + _passwordFormat); + sb.AppendLine("_passwordStrengthRegularExpression=" + _passwordStrengthRegularExpression); + sb.AppendLine("_requiresQuestionAndAnswer=" + _requiresQuestionAndAnswer); + sb.AppendLine("_requiresUniqueEmail=" + _requiresUniqueEmail); + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 0f4cd7c242..a84734d1f7 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -206,6 +206,9 @@ namespace Umbraco.Core.Services case StringPropertyMatchType.EndsWith: query.Where(member => member.Email.EndsWith(emailStringToMatch)); break; + case StringPropertyMatchType.Wildcard: + query.Where(member => member.Email.SqlWildcard(emailStringToMatch, TextColumnType.NVarchar)); + break; default: throw new ArgumentOutOfRangeException("matchType"); } @@ -235,6 +238,9 @@ namespace Umbraco.Core.Services case StringPropertyMatchType.EndsWith: query.Where(member => member.Username.EndsWith(login)); break; + case StringPropertyMatchType.Wildcard: + query.Where(member => member.Email.SqlWildcard(login, TextColumnType.NVarchar)); + break; default: throw new ArgumentOutOfRangeException("matchType"); } diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs index 500b33501c..06e44be215 100644 --- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs +++ b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Provider; using System.Diagnostics; using System.Linq; using System.Text; @@ -14,6 +16,48 @@ namespace Umbraco.Tests.Membership [TestFixture] public class MembershipProviderBaseTests { + //[Test] + //public void Change_Password_Base_Validation() + + //[Test] + //public void ChangePasswordQuestionAndAnswer_Base_Validation() + + //[Test] + //public void CreateUser_Base_Validation() + + [Test] + public void Sets_Defaults() + { + var provider = new TestProvider(); + provider.Initialize("test", new NameValueCollection()); + + Assert.AreEqual("test", provider.Name); + Assert.AreEqual(MembershipProviderBase.GetDefaultAppName(), provider.ApplicationName); + Assert.AreEqual(false, provider.EnablePasswordRetrieval); + Assert.AreEqual(false, provider.EnablePasswordReset); + Assert.AreEqual(false, provider.RequiresQuestionAndAnswer); + Assert.AreEqual(true, provider.RequiresUniqueEmail); + Assert.AreEqual(5, provider.MaxInvalidPasswordAttempts); + Assert.AreEqual(10, provider.PasswordAttemptWindow); + Assert.AreEqual(provider.DefaultMinPasswordLength, provider.MinRequiredPasswordLength); + Assert.AreEqual(provider.DefaultMinNonAlphanumericChars, provider.MinRequiredNonAlphanumericCharacters); + Assert.AreEqual(null, provider.PasswordStrengthRegularExpression); + Assert.AreEqual(provider.DefaultUseLegacyEncoding, provider.UseLegacyEncoding); + Assert.AreEqual(MembershipPasswordFormat.Hashed, provider.PasswordFormat); + } + + [Test] + public void Throws_Exception_With_Hashed_Password_And_Password_Retrieval() + { + var provider = new TestProvider(); + + Assert.Throws(() => provider.Initialize("test", new NameValueCollection() + { + {"enablePasswordRetrieval", "true"}, + {"passwordFormat", "Hashed"} + })); + } + [TestCase("hello", 0, "", 5, true)] [TestCase("hello", 0, "", 4, true)] [TestCase("hello", 0, "", 6, false)] @@ -65,5 +109,88 @@ namespace Umbraco.Tests.Membership Assert.AreEqual(salt, initSalt); } + 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(); + } + + public override bool ValidateUser(string username, string password) + { + throw new NotImplementedException(); + } + + public override bool UnlockUser(string userName) + { + throw new NotImplementedException(); + } + + public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) + { + throw new NotImplementedException(); + } + + public override MembershipUser GetUser(string username, bool userIsOnline) + { + throw new NotImplementedException(); + } + + public override string GetUserNameByEmail(string email) + { + throw new NotImplementedException(); + } + + public override bool DeleteUser(string username, bool deleteAllRelatedData) + { + throw new NotImplementedException(); + } + + public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) + { + throw new NotImplementedException(); + } + + public override int GetNumberOfUsersOnline() + { + throw new NotImplementedException(); + } + + public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotImplementedException(); + } + + public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotImplementedException(); + } + + protected override bool PerformChangePassword(string username, string oldPassword, string newPassword) + { + throw new NotImplementedException(); + } + + protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + throw new NotImplementedException(); + } + + protected override MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + throw new NotImplementedException(); + } + } + } } diff --git a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs index f423d95fbd..23ee46fabc 100644 --- a/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs +++ b/src/Umbraco.Web/Security/Providers/MembersMembershipProvider.cs @@ -11,6 +11,8 @@ using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Models.Membership; @@ -19,25 +21,8 @@ namespace Umbraco.Web.Security.Providers /// /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) /// - internal class MembersMembershipProvider : MembershipProvider + internal class MembersMembershipProvider : UmbracoMembershipProviderBase { - #region Fields - private string _applicationName; - private bool _enablePasswordReset; - private bool _enablePasswordRetrieval; - private int _maxInvalidPasswordAttempts; - private int _minRequiredNonAlphanumericCharacters; - private int _minRequiredPasswordLength; - private int _passwordAttemptWindow; - - private MembershipPasswordFormat _passwordFormat; - - private string _passwordStrengthRegularExpression; - private bool _requiresQuestionAndAnswer; - private bool _requiresUniqueEmail; - - #endregion - private IMembershipMemberService _memberService; protected IMembershipMemberService MemberService @@ -45,125 +30,8 @@ namespace Umbraco.Web.Security.Providers get { return _memberService ?? (_memberService = ApplicationContext.Current.Services.MemberService); } } - /// - /// The name of the application using the custom membership provider. - /// - /// - /// The name of the application using the custom membership provider. - public override string ApplicationName + public string ProviderName { - get { return _applicationName; } - set { _applicationName = value; } - } - - /// - /// Indicates whether the membership provider is configured to allow users to reset their passwords. - /// - /// - /// true if the membership provider supports password reset; otherwise, false. The default is true. - public override bool EnablePasswordReset - { - get { return _enablePasswordReset; } - } - - /// - /// Indicates whether the membership provider is configured to allow users to retrieve their passwords. - /// - /// - /// true if the membership provider is configured to support password retrieval; otherwise, false. The default is false. - public override bool EnablePasswordRetrieval - { - get { return _enablePasswordRetrieval; } - } - - /// - /// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out. - /// - /// - /// The number of invalid password or password-answer attempts allowed before the membership user is locked out. - public override int MaxInvalidPasswordAttempts - { - get { return _maxInvalidPasswordAttempts; } - } - - /// - /// Gets the minimum number of special characters that must be present in a valid password. - /// - /// - /// The minimum number of special characters that must be present in a valid password. - public override int MinRequiredNonAlphanumericCharacters - { - get { return _minRequiredNonAlphanumericCharacters; } - } - - /// - /// Gets the minimum length required for a password. - /// - /// - /// The minimum length required for a password. - public override int MinRequiredPasswordLength - { - get { return _minRequiredPasswordLength; } - } - - /// - /// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. - /// - /// - /// The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out. - public override int PasswordAttemptWindow - { - get { return _passwordAttemptWindow; } - } - - /// - /// Gets a value indicating the format for storing passwords in the membership data store. - /// - /// - /// One of the values indicating the format for storing passwords in the data store. - public override MembershipPasswordFormat PasswordFormat - { - get { return _passwordFormat; } - } - - /// - /// Gets the regular expression used to evaluate a password. - /// - /// - /// A regular expression used to evaluate a password. - public override string PasswordStrengthRegularExpression - { - get { return _passwordStrengthRegularExpression; } - } - - /// - /// Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval. - /// - /// - /// true if a password answer is required for password reset and retrieval; otherwise, false. The default is true. - public override bool RequiresQuestionAndAnswer - { - get { return _requiresQuestionAndAnswer; } - } - - /// - /// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name. - /// - /// - /// true if the membership provider requires a unique e-mail address; otherwise, false. The default is true. - public override bool RequiresUniqueEmail - { - get { return _requiresUniqueEmail; } - } - - /// - /// The default Umbraco member type alias to create when registration is performed using .net Membership - /// - /// - /// Member type alias - public string DefaultMemberTypeAlias { get; private set; } - - public string ProviderName { get { return "MembersMembershipProvider"; } } @@ -186,80 +54,127 @@ namespace Umbraco.Web.Security.Providers // Initialize base provider class base.Initialize(name, config); - _applicationName = string.IsNullOrEmpty(config["applicationName"]) ? GetDefaultAppName() : config["applicationName"]; + //// test for membertype (if not specified, choose the first member type available) + //if (config["defaultMemberTypeAlias"] != null) + // _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; + //else if (MemberType.GetAll.Length == 1) + // _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"); - _enablePasswordRetrieval = GetBooleanValue(config, "enablePasswordRetrieval", false); - _enablePasswordReset = GetBooleanValue(config, "enablePasswordReset", false); - _requiresQuestionAndAnswer = GetBooleanValue(config, "requiresQuestionAndAnswer", false); - _requiresUniqueEmail = GetBooleanValue(config, "requiresUniqueEmail", true); - _maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0); - _passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0); - _minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", 7, true, 0x80); - _minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", 1, true, 0x80); - _passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"]; - - // make sure password format is Hashed by default. - var str = config["passwordFormat"] ?? "Hashed"; - - LogHelper.Debug("Loaded membership provider properties"); - LogHelper.Debug(ToString()); - - switch (str.ToLower()) + // test for approve status + if (config["umbracoApprovePropertyTypeAlias"] != null) { - case "clear": - _passwordFormat = MembershipPasswordFormat.Clear; - break; - case "encrypted": - _passwordFormat = MembershipPasswordFormat.Encrypted; - break; - case "hashed": - _passwordFormat = MembershipPasswordFormat.Hashed; - break; - default: - var e = new ProviderException("Provider bad password format"); - LogHelper.Error(e.Message, e); - throw e; + ApprovedPropertyTypeAlias = config["umbracoApprovePropertyTypeAlias"]; } - - if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval) + // test for lock attempts + if (config["umbracoLockPropertyTypeAlias"] != null) { - var e = new ProviderException("Provider can not retrieve hashed password"); - LogHelper.Error(e.Message, e); - throw e; + LockPropertyTypeAlias = config["umbracoLockPropertyTypeAlias"]; } - - // TODO: rationalise what happens when no member alias is specified.... - DefaultMemberTypeAlias = config["defaultMemberTypeAlias"]; - - LogHelper.Debug("Finished initialising member ship provider " + GetType().FullName); + 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"]; + } } - public override string ToString() + /// + /// Processes a request to update the password for a membership user. + /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// + protected override bool PerformChangePassword(string username, string oldPassword, string newPassword) { - var result = base.ToString(); + //NOTE: due to backwards compatibilty reasons (and UX reasons), this provider doesn't care about the old password and + // allows simply setting the password manually so we don't really care about the old password. + // This is allowed based on the overridden AllowManuallyChangingPassword option. - result += "_applicationName =" + _applicationName + Environment.NewLine; - result += "_enablePasswordReset=" + _enablePasswordReset + Environment.NewLine; - result += "_enablePasswordRetrieval=" + _enablePasswordRetrieval + Environment.NewLine; - result += "_maxInvalidPasswordAttempts=" + _maxInvalidPasswordAttempts + Environment.NewLine; - result += "_minRequiredNonAlphanumericCharacters=" + _minRequiredNonAlphanumericCharacters + Environment.NewLine; - result += "_minRequiredPasswordLength=" + _minRequiredPasswordLength + Environment.NewLine; - result += "_passwordAttemptWindow=" + _passwordAttemptWindow + Environment.NewLine; + // 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; - result += "_passwordFormat=" + _passwordFormat + Environment.NewLine; + var args = new ValidatePasswordEventArgs(username, newPassword, false); + OnValidatingPassword(args); - result += "_passwordStrengthRegularExpression=" + _passwordStrengthRegularExpression + Environment.NewLine; - result += "_requiresQuestionAndAnswer=" + _requiresQuestionAndAnswer + Environment.NewLine; - result += "_requiresUniqueEmail=" + _requiresUniqueEmail + Environment.NewLine; - result += "DefaultMemberTypeAlias=" + DefaultMemberTypeAlias + Environment.NewLine; + if (args.Cancel) + { + if (args.FailureInformation != null) + throw args.FailureInformation; + throw new MembershipPasswordException("Change password canceled due to password validation failure."); + } - return result; + string salt; + var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); + + m.Password = FormatPasswordForStorage(encodedPassword, salt); + m.LastPasswordChangeDate = DateTime.Now; + + _memberService.Save(m); + + return true; + } + + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + var member = MemberService.GetByUsername(username); + if (member == null) + { + return false; + } + + member.PasswordQuestion = newPasswordQuestion; + member.PasswordAnswer = newPasswordAnswer; + + MemberService.Save(member); + + return true; } /// /// Adds a new membership user to the data source with the specified member type /// - /// A specific member type to create the member for + /// A specific member type to create the member for /// The user name for the new user. /// The password for the new user. /// The e-mail address for the new user. @@ -271,48 +186,11 @@ namespace Umbraco.Web.Security.Providers /// /// A object populated with the information for the newly created user. /// - public MembershipUser CreateUser(string memberType, string username, string password, string email, string passwordQuestion, string passwordAnswer, - bool isApproved, object providerUserKey, out MembershipCreateStatus status) + protected override MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, + string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { - LogHelper.Debug("Member signup requested: username -> " + username + ". email -> " + email); - - // Validate password - if (IsPasswordValid(password) == false) - { - status = MembershipCreateStatus.InvalidPassword; - return null; - } - - // Validate email - if (IsEmaiValid(email) == false) - { - status = MembershipCreateStatus.InvalidEmail; - return null; - } - - // Make sure username isn't all whitespace - if (string.IsNullOrWhiteSpace(username.Trim())) - { - status = MembershipCreateStatus.InvalidUserName; - return null; - } - - // Check password question - if (string.IsNullOrWhiteSpace(passwordQuestion) && _requiresQuestionAndAnswer) - { - status = MembershipCreateStatus.InvalidQuestion; - return null; - } - - // Check password answer - if (string.IsNullOrWhiteSpace(passwordAnswer) && _requiresQuestionAndAnswer) - { - status = MembershipCreateStatus.InvalidAnswer; - return null; - } - // See if the user already exists - if (MemberService.GetByUsername(username) != null) + if (MemberService.Exists(username)) { status = MembershipCreateStatus.DuplicateUserName; LogHelper.Warn("Cannot create member as username already exists: " + username); @@ -328,14 +206,16 @@ namespace Umbraco.Web.Security.Providers return null; } - var member = MemberService.CreateMember(email, username, password, memberType); + string salt; + var encodedPassword = EncryptOrHashNewPassword(password, out salt); - member.IsApproved = isApproved; + var member = MemberService.CreateMember(email, username, encodedPassword, memberTypeAlias); + member.PasswordQuestion = passwordQuestion; member.PasswordAnswer = passwordAnswer; - - //encrypts/hashes the password depending on the settings - member.Password = EncryptOrHashPassword(member.Password); + member.IsApproved = isApproved; + member.LastLoginDate = DateTime.Now; + member.LastPasswordChangeDate = DateTime.Now; MemberService.Save(member); @@ -344,68 +224,72 @@ namespace Umbraco.Web.Security.Providers } /// - /// Adds a new membership user to the data source. + /// Removes a user from the membership data source. /// - /// The user name for the new user. - /// The password for the new user. - /// The e-mail address for the new user. - /// The password question for the new user. - /// The password answer for the new user - /// Whether or not the new user is approved to be validated. - /// The unique identifier from the membership data source for the user. - /// A enumeration value indicating whether the user was created successfully. + /// The name of the user to delete. + /// + /// TODO: This setting currently has no effect + /// /// - /// A object populated with the information for the newly created user. + /// true if the user was successfully deleted; otherwise, false. /// - public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, - bool isApproved, object providerUserKey, out MembershipCreateStatus status) + public override bool DeleteUser(string username, bool deleteAllRelatedData) { - return CreateUser(DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); + var member = MemberService.GetByUsername(username); + if (member == null) return false; + + MemberService.Delete(member); + return true; } /// - /// Processes a request to update the password question and answer for a membership user. + /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. /// - /// The user to change the password question and answer for. - /// The password for the specified user. - /// The new password question for the specified user. - /// The new password answer for the specified user. + /// The e-mail address to search for. + /// 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. /// - /// true if the password question and answer are updated successfully; otherwise, false. + /// A collection that contains a page of pageSize objects beginning at the page specified by pageIndex. /// - public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, - string newPasswordAnswer) + public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { - if (_requiresQuestionAndAnswer == false) + var byEmail = MemberService.FindMembersByEmail(emailToMatch, StringPropertyMatchType.Wildcard).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) { - throw new NotSupportedException("Updating the password Question and Answer is not available if requiresQuestionAndAnswer is not set in web.config"); + collection.Add(m.AsConcreteMembershipUser()); } - - if (ValidateUser(username, password) == false) - { - throw new MembershipPasswordException("Invalid username and password combinatio"); - } - - var member = MemberService.GetByUsername(username); - var encodedPassword = EncryptOrHashPassword(password); - - if (member.Password == encodedPassword) - { - member.PasswordQuestion = newPasswordQuestion; - member.PasswordAnswer = newPasswordAnswer; - - MemberService.Save(member); - - return true; - } - else - { - //TODO: Throw here? or just return false; - } - - return false; + return collection; } + /// + /// Gets a collection of membership users where the user name contains the specified user name to match. + /// + /// The user name to search for. + /// 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 FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + { + var byEmail = MemberService.FindMembersByUsername(usernameToMatch, StringPropertyMatchType.Wildcard).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) + { + collection.Add(m.AsConcreteMembershipUser()); + } + return collection; + } + /// /// Gets the password for the specified user name from the data source. /// @@ -416,59 +300,22 @@ namespace Umbraco.Web.Security.Providers /// public override string GetPassword(string username, string answer) { - if (_enablePasswordRetrieval == false) + if (EnablePasswordRetrieval == false) throw new ProviderException("Password Retrieval Not Enabled."); - if (_passwordFormat == MembershipPasswordFormat.Hashed) + if (PasswordFormat == MembershipPasswordFormat.Hashed) throw new ProviderException("Cannot retrieve Hashed passwords."); var member = MemberService.GetByUsername(username); - if (_requiresQuestionAndAnswer && member.PasswordAnswer != answer) + if (RequiresQuestionAndAnswer && member.PasswordAnswer != answer) { throw new ProviderException("Password retrieval answer doesn't match"); } return member.Password; } - - /// - /// Processes a request to update the password for a membership user. - /// - /// The user to update the password for. - /// The current password for the specified user. - /// The new password for the specified user. - /// - /// true if the password was updated successfully; otherwise, false. - /// - public override bool ChangePassword(string username, string oldPassword, string newPassword) - { - // Validate new password - if (IsPasswordValid(newPassword) == false) - { - var e = new MembershipPasswordException("Change password canceled due to new password validation failure."); - LogHelper.WarnWithException(e.Message, e); - throw e; - } - - var member = MemberService.GetByUsername(username); - if (member == null) return false; - - var encodedPassword = EncryptOrHashPassword(oldPassword); - - if (member.Password == encodedPassword) - { - - member.Password = EncryptOrHashPassword(newPassword); - MemberService.Save(member); - - return true; - } - - LogHelper.Warn("Can't change password as old password was incorrect"); - return false; - } - + /// /// Resets a user's password to a new, automatically generated password. /// @@ -477,30 +324,34 @@ namespace Umbraco.Web.Security.Providers /// The new password for the specified user. public override string ResetPassword(string username, string answer) { - if (_enablePasswordReset == false) - throw new ProviderException("Password reset is Not Enabled."); + //TODO: Get logic from other provider - var member = MemberService.GetByUsername(username); + throw new NotImplementedException(); - if (member == null) - throw new ProviderException("The supplied user is not found"); + //if (EnablePasswordReset == false) + // throw new ProviderException("Password reset is Not Enabled."); - if(member.IsLockedOut) - throw new ProviderException("The member is locked out."); + //var member = MemberService.GetByUsername(username); - if (_requiresQuestionAndAnswer == false || (_requiresQuestionAndAnswer && answer == member.PasswordAnswer)) - { - member.Password = - EncryptOrHashPassword(Membership.GeneratePassword(_minRequiredPasswordLength, - _minRequiredNonAlphanumericCharacters)); - MemberService.Save(member); - } - else - { - throw new MembershipPasswordException("Incorrect password answer"); - } + //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; + //return null; } /// @@ -511,8 +362,8 @@ namespace Umbraco.Web.Security.Providers /// 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); + //var member = user.AsIMember(); + //MemberService.Save(member); } /// @@ -532,7 +383,8 @@ namespace Umbraco.Web.Security.Providers if (member.IsLockedOut) throw new ProviderException("The member is locked out."); - var encodedPassword = EncryptOrHashPassword(password); + string salt; + var encodedPassword = EncryptOrHashNewPassword(password, out salt); var authenticated = (encodedPassword == member.Password); @@ -645,22 +497,7 @@ namespace Umbraco.Web.Security.Providers return member == null ? null : member.Username; } - /// - /// Removes a user from the membership data source. - /// - /// The name of the user to delete. - /// true to delete data related to the user from the database; false to leave data related to the user in the database. - /// - /// true if the user was successfully deleted; otherwise, false. - /// - public override bool DeleteUser(string username, bool deleteAllRelatedData) - { - var member = MemberService.GetByUsername(username); - if (member == null) return false; - - MemberService.Delete(member); - return true; - } + /// /// Gets a collection of all the users in the data source in pages of data. @@ -687,182 +524,14 @@ namespace Umbraco.Web.Security.Providers throw new System.NotImplementedException(); } - /// - /// Gets a collection of membership users where the user name contains the specified user name to match. - /// - /// The user name to search for. - /// 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 FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + + public override string ToString() { - throw new System.NotImplementedException(); + var result = base.ToString(); + var sb = new StringBuilder(result); + sb.AppendLine("DefaultMemberTypeAlias=" + DefaultMemberTypeAlias); + return sb.ToString(); } - /// - /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. - /// - /// The e-mail address to search for. - /// 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 FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) - { - var byEmail = MemberService.FindMembersByEmail(emailToMatch).ToArray(); - totalRecords = byEmail.Length; - var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); - - var collection = new MembershipUserCollection(); - foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) - { - collection.Add(m.AsConcreteMembershipUser()); - } - return collection; - } - - #region Private methods - - private bool IsPasswordValid(string password) - { - if (_minRequiredNonAlphanumericCharacters > 0) - { - var nonAlphaNumeric = Regex.Replace(password, "[a-zA-Z0-9]", "", RegexOptions.Multiline | RegexOptions.IgnoreCase); - if (nonAlphaNumeric.Length < _minRequiredNonAlphanumericCharacters) - { - return false; - } - } - - var valid = true; - if(string.IsNullOrEmpty(_passwordStrengthRegularExpression) == false) - { - valid = Regex.IsMatch(password, _passwordStrengthRegularExpression, RegexOptions.Compiled); - } - - return valid && password.Length >= _minRequiredPasswordLength; - } - - private bool IsEmaiValid(string email) - { - const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" - + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? - /// Encodes the password. - /// - /// The password. - /// The encoded password. - private string EncryptOrHashPassword(string password) - { - var encodedPassword = password; - switch (PasswordFormat) - { - case MembershipPasswordFormat.Clear: - break; - case MembershipPasswordFormat.Encrypted: - encodedPassword = - Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password))); - break; - case MembershipPasswordFormat.Hashed: - var hash = new HMACSHA1 {Key = Encoding.Unicode.GetBytes(password)}; - encodedPassword = - Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password))); - break; - default: - throw new ProviderException("Unsupported password format."); - } - return encodedPassword; - } - - /// - /// Gets the boolean value. - /// - /// The config. - /// Name of the value. - /// if set to true [default value]. - /// - private bool GetBooleanValue(NameValueCollection config, string valueName, bool defaultValue) - { - bool flag; - var str = config[valueName]; - if (str == null) - return defaultValue; - - if (bool.TryParse(str, out flag) == false) - { - throw new ProviderException("Value must be boolean."); - } - return flag; - } - - /// - /// Gets the int value. - /// - /// The config. - /// Name of the value. - /// The default value. - /// if set to true [zero allowed]. - /// The max value allowed. - /// - private int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed) - { - int num; - var s = config[valueName]; - if (s == null) - { - return defaultValue; - } - if (int.TryParse(s, out num) == false) - { - if (zeroAllowed) - { - throw new ProviderException("Value must be non negative integer"); - } - throw new ProviderException("Value must be positive integer"); - } - if (zeroAllowed && (num < 0)) - { - throw new ProviderException("Value must be non negativeinteger"); - } - if (zeroAllowed == false && (num <= 0)) - { - throw new ProviderException("Value must be positive integer"); - } - if ((maxValueAllowed > 0) && (num > maxValueAllowed)) - { - throw new ProviderException("Value too big"); - } - return num; - } - - - /// - /// Gets the name of the default app. - /// - /// - private string GetDefaultAppName() - { - try - { - var applicationVirtualPath = HostingEnvironment.ApplicationVirtualPath; - return string.IsNullOrEmpty(applicationVirtualPath) ? "/" : applicationVirtualPath; - } - catch - { - return "/"; - } - } - - #endregion } } \ No newline at end of file diff --git a/src/umbraco.providers/UsersMembershipProvider.cs b/src/umbraco.providers/UsersMembershipProvider.cs index 278aff9871..e862bf5b19 100644 --- a/src/umbraco.providers/UsersMembershipProvider.cs +++ b/src/umbraco.providers/UsersMembershipProvider.cs @@ -22,7 +22,7 @@ namespace umbraco.providers /// /// Override to maintain backwards compatibility with 0 required non-alphanumeric chars /// - protected override int DefaultMinNonAlphanumericChars + public override int DefaultMinNonAlphanumericChars { get { return 0; } } @@ -30,7 +30,7 @@ namespace umbraco.providers /// /// Override to maintain backwards compatibility with only 4 required length /// - protected override int DefaultMinPasswordLength + public override int DefaultMinPasswordLength { get { return 4; } } @@ -38,7 +38,7 @@ namespace umbraco.providers /// /// Override to maintain backwards compatibility /// - protected override bool DefaultUseLegacyEncoding + public override bool DefaultUseLegacyEncoding { get { return true; } } @@ -110,7 +110,7 @@ namespace umbraco.providers /// /// true if the password question and answer are updated successfully; otherwise, false. /// - public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { throw new Exception("The method or operation is not implemented."); } @@ -129,7 +129,7 @@ namespace umbraco.providers /// /// A object populated with the information for the newly created user. /// - public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + protected override MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { var args = new ValidatePasswordEventArgs(username, password, true); OnValidatingPassword(args); diff --git a/src/umbraco.providers/members/MembersMembershipProvider.cs b/src/umbraco.providers/members/UmbracoMembershipProvider.cs similarity index 71% rename from src/umbraco.providers/members/MembersMembershipProvider.cs rename to src/umbraco.providers/members/UmbracoMembershipProvider.cs index a14b47d84d..62562a85b0 100644 --- a/src/umbraco.providers/members/MembersMembershipProvider.cs +++ b/src/umbraco.providers/members/UmbracoMembershipProvider.cs @@ -1,1031 +1,958 @@ -#region namespace -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Security; -using System.Configuration; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Security; -using umbraco.BusinessLogic; -using System.Security.Cryptography; -using System.Web.Util; -using System.Collections.Specialized; -using System.Configuration.Provider; -using System.Security; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using Member = umbraco.cms.businesslogic.member.Member; -using MemberType = umbraco.cms.businesslogic.member.MemberType; - -#endregion - -namespace umbraco.providers.members -{ - /// - /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) - /// - - public class UmbracoMembershipProvider : MembershipProviderBase - { - #region Fields - - //Set the defaults! - private string _defaultMemberTypeAlias = "Member"; - private string _lockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut; - private string _lastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate; - private string _failedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts; - private string _approvedPropertyTypeAlias = Constants.Conventions.Member.IsApproved; - private string _commentPropertyTypeAlias = Constants.Conventions.Member.Comments; - private string _lastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate; - private string _lastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate; - private string _passwordRetrievalQuestionPropertyTypeAlias = Constants.Conventions.Member.PasswordQuestion; - private string _passwordRetrievalAnswerPropertyTypeAlias = Constants.Conventions.Member.PasswordAnswer; - - private string _providerName = Member.UmbracoMemberProviderName; - - //Need to expose these publicly so we know what field aliases to use in the editor, we only care about these 3 fields - // because they are the only 'settable' provider properties that are not stored against the IMember directly (i.e. they are - // property type properties). - - public string LockPropertyTypeAlias - { - get { return _lockPropertyTypeAlias; } - } - - public string ApprovedPropertyTypeAlias - { - get { return _approvedPropertyTypeAlias; } - } - - public string CommentPropertyTypeAlias - { - get { return _commentPropertyTypeAlias; } - } - - #endregion - - /// - /// Override to maintain backwards compatibility with 0 required non-alphanumeric chars - /// - protected override int DefaultMinNonAlphanumericChars - { - get { return 0; } - } - - /// - /// Override to maintain backwards compatibility with only 4 required length - /// - protected override int DefaultMinPasswordLength - { - get { return 4; } - } - - /// - /// Override to maintain backwards compatibility - /// - protected override bool DefaultUseLegacyEncoding - { - get { return true; } - } - - /// - /// For backwards compatibility, this provider supports this option - /// - internal override bool AllowManuallyChangingPassword - { - get { return true; } - } - - #region Initialization Method - /// - /// Initializes the provider. - /// - /// The friendly name of the provider. - /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. - /// The name of the provider is null. - /// An attempt is made to call - /// on a provider after the provider - /// has already been initialized. - /// The name of the provider has a length of zero. - public override void Initialize(string name, NameValueCollection config) - { - // Intialize values from web.config - if (config == null) throw new ArgumentNullException("config"); - - if (string.IsNullOrEmpty(name)) name = "UmbracoMembershipProvider"; - - base.Initialize(name, config); - - _providerName = name; - - // test for membertype (if not specified, choose the first member type available) - if (config["defaultMemberTypeAlias"] != null) - _defaultMemberTypeAlias = config["defaultMemberTypeAlias"]; - else if (MemberType.GetAll.Length == 1) - _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"]; - } - - } - #endregion - - #region Methods - - /// - /// Processes a request to update the password for a membership user. - /// - /// The user to update the password for. - /// This property is ignore for this provider - /// The new password for the specified user. - /// - /// true if the password was updated successfully; otherwise, false. - /// - protected override bool PerformChangePassword(string username, string oldPassword, string newPassword) - { - //NOTE: due to backwards compatibilty reasons, this provider doesn't care about the old password and - // allows simply setting the password manually so we don't really care about the old password. - // This is allowed based on the overridden AllowManuallyChangingPassword option. - - // 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( - FormatPasswordForStorage(encodedPassword, salt)); - - UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); - - m.Save(); - - return true; - } - - /// - /// Processes a request to update the password question and answer for a membership user. - /// - /// The user to change the password question and answer for. - /// The password for the specified user. - /// The new password question for the specified user. - /// The new password answer for the specified user. - /// - /// true if the password question and answer are updated successfully; otherwise, false. - /// - public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) - { - if (!String.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) && !String.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias)) - { - if (ValidateUser(username, password)) - { - Member m = Member.GetMemberFromLoginName(username); - if (m != null) - { - UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, newPasswordQuestion); - UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, newPasswordAnswer); - m.Save(); - return true; - } - else - { - throw new MembershipPasswordException("The supplied user is not found!"); - } - } - else { - throw new MembershipPasswordException("Invalid user/password combo"); - } - - } - else - { - throw new NotSupportedException("Updating the password Question and Answer is not valid if the properties aren't set in the config file"); - } - } - - /// - /// Adds a new membership user to the data source. - /// - /// - /// The user name for the new user. - /// The password for the new user. - /// The e-mail address for the new user. - /// The password question for the new user. - /// The password answer for the new user - /// Whether or not the new user is approved to be validated. - /// The unique identifier from the membership data source for the user. - /// A enumeration value indicating whether the user was created successfully. - /// - /// A object populated with the information for the newly created user. - /// - public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, - string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) - { - - var args = new ValidatePasswordEventArgs(username, password, true); - OnValidatingPassword(args); - if (args.Cancel) - { - status = MembershipCreateStatus.InvalidPassword; - return null; - } - - if (Member.GetMemberFromLoginName(username) != null) - { - status = MembershipCreateStatus.DuplicateUserName; - } - else if (Member.GetMemberFromEmail(email) != null && RequiresUniqueEmail) - { - status = MembershipCreateStatus.DuplicateEmail; - } - else - { - var memberType = MemberType.GetByAlias(memberTypeAlias); - if (memberType == null) - { - throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); - } - - var m = Member.MakeNew(username, email, memberType, User.GetUser(0)); - - string salt; - var encodedPassword = EncryptOrHashNewPassword(password, out salt); - //set the password on the member - m.ChangePassword( - FormatPasswordForStorage(encodedPassword, salt)); - - // custom fields - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, passwordQuestion); - } - - if (string.IsNullOrEmpty(_passwordRetrievalAnswerPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, _passwordRetrievalAnswerPropertyTypeAlias, passwordAnswer); - } - - if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, ApprovedPropertyTypeAlias, isApproved ? 1 : 0); - } - - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, _lastLoginPropertyTypeAlias, DateTime.Now); - } - - if (string.IsNullOrEmpty(_lastPasswordChangedPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); - } - - var mUser = ConvertToMembershipUser(m); - - // save - m.Save(); - - status = MembershipCreateStatus.Success; - - return mUser; - } - return null; - } - - /// - /// Adds a new membership user to the data source. - /// - /// The user name for the new user. - /// The password for the new user. - /// The e-mail address for the new user. - /// The password question for the new user. - /// The password answer for the new user - /// Whether or not the new user is approved to be validated. - /// The unique identifier from the membership data source for the user. - /// A enumeration value indicating whether the user was created successfully. - /// - /// A object populated with the information for the newly created user. - /// - public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, - string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) - { - return CreateUser(_defaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); - } - - /// - /// Removes a user from the membership data source. - /// - /// The name of the user to delete. - /// true to delete data related to the user from the database; false to leave data related to the user in the database. - /// - /// true if the user was successfully deleted; otherwise, false. - /// - public override bool DeleteUser(string username, bool deleteAllRelatedData) - { - var m = Member.GetMemberFromLoginName(username); - if (m == null) return false; - m.delete(); - return true; - } - - /// - /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. - /// - /// The e-mail address to search for. - /// 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 FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) - { - var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByEmail(emailToMatch).ToArray(); - totalRecords = byEmail.Length; - var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); - - var collection = new MembershipUserCollection(); - foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) - { - collection.Add(ConvertToMembershipUser(m)); - } - return collection; - } - - /// - /// Gets a collection of membership users where the user name contains the specified user name to match. - /// - /// The user name to search for. - /// 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 FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) - { - var counter = 0; - var startIndex = pageSize * pageIndex; - var endIndex = startIndex + pageSize - 1; - var membersList = new MembershipUserCollection(); - var memberArray = Member.GetMemberByName(usernameToMatch, false); - totalRecords = memberArray.Length; - - foreach (var m in memberArray) - { - if (counter >= startIndex) - membersList.Add(ConvertToMembershipUser(m)); - if (counter >= endIndex) break; - counter++; - } - return membersList; - } - - /// - /// 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 counter = 0; - var startIndex = pageSize * pageIndex; - var endIndex = startIndex + pageSize - 1; - var membersList = new MembershipUserCollection(); - var memberArray = Member.GetAll; - totalRecords = memberArray.Length; - - foreach (var m in memberArray) - { - if (counter >= startIndex) - membersList.Add(ConvertToMembershipUser(m)); - if (counter >= endIndex) break; - counter++; - } - return membersList; - - } - - /// - /// Gets the number of users currently accessing the application. - /// - /// - /// The number of users currently accessing the application. - /// - public override int GetNumberOfUsersOnline() - { - return Member.CachedMembers().Count; - } - - /// - /// Gets the password for the specified user name from the data source. - /// - /// The user to retrieve the password for. - /// The password answer for the user. - /// - /// 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 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(); - } - - /// - /// Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. - /// - /// The name of the 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(string username, bool userIsOnline) - { - if (String.IsNullOrEmpty(username)) - return null; - Member m = Member.GetMemberFromLoginName(username); - if (m == null) return null; - else return ConvertToMembershipUser(m); - } - - /// - /// 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 asGuid = providerUserKey.TryConvertTo(); - if (asGuid.Success) - { - var m = new Member(asGuid.Result); - return ConvertToMembershipUser(m); - } - var asInt = providerUserKey.TryConvertTo(); - if (asInt.Success) - { - var m = new Member(asInt.Result); - return ConvertToMembershipUser(m); - } - throw new InvalidOperationException("The " + GetType() + " provider only supports GUID or Int as a providerUserKey"); - - } - - - /// - /// Gets the user name associated with the specified e-mail address. - /// - /// The e-mail address to search for. - /// - /// The user name associated with the specified e-mail address. If no match is found, return null. - /// - public override string GetUserNameByEmail(string email) - { - Member m = Member.GetMemberFromEmail(email); - return m == null ? null : m.LoginName; - } - - /// - /// 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) - { - 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) - //{ - // UpdateFailureCount(username, "passwordAnswer"); - - // 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"); - - 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"); - } - } - - string salt; - var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); - //set the password on the member - m.ChangePassword( - FormatPasswordForStorage(encodedPassword, salt)); - - if (string.IsNullOrEmpty(_lastPasswordChangedPropertyTypeAlias) == false) - { - UpdateMemberProperty(m, _lastPasswordChangedPropertyTypeAlias, DateTime.Now); - } - - m.Save(); - - return newPassword; - } - - /// - /// 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) - { - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - var m = Member.GetMemberFromLoginName(userName); - if (m != null) - { - UpdateMemberProperty(m, LockPropertyTypeAlias, 0); - m.Save(); - return true; - } - throw new Exception(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."); - } - - /// - /// Updates e-mail and potentially 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 = Member.GetMemberFromLoginName(user.UserName); - m.Email = user.Email; - - // if supported, update approve status - UpdateMemberProperty(m, ApprovedPropertyTypeAlias, user.IsApproved ? 1 : 0); - - // if supported, update lock status - UpdateMemberProperty(m, LockPropertyTypeAlias, user.IsLockedOut ? 1 : 0); - if (user.IsLockedOut) - { - UpdateMemberProperty(m, _lastLockedOutPropertyTypeAlias, DateTime.Now); - } - - // if supported, update comment - UpdateMemberProperty(m, CommentPropertyTypeAlias, user.Comment); - - m.Save(); - } - - private static void UpdateMemberProperty(Member m, string propertyAlias, object propertyValue) - { - if (string.IsNullOrEmpty(propertyAlias) == false) - { - if (m.getProperty(propertyAlias) != null) - { - m.getProperty(propertyAlias).Value = propertyValue; - } - } - } - - private static string GetMemberProperty(Member m, string propertyAlias, bool isBool) - { - if (string.IsNullOrEmpty(propertyAlias) == false) - { - if (m.getProperty(propertyAlias) != null && - m.getProperty(propertyAlias).Value != null) - { - if (isBool) - { - // Umbraco stored true as 1, which means it can be bool.tryParse'd - return m.getProperty(propertyAlias).Value.ToString().Replace("1", "true").Replace("0", "false"); - } - return m.getProperty(propertyAlias).Value.ToString(); - } - } - - return null; - } - - private static string GetMemberProperty(IMember m, string propertyAlias, bool isBool) - { - if (string.IsNullOrEmpty(propertyAlias) == false) - { - if (m.Properties[propertyAlias] != null && - m.Properties[propertyAlias].Value != null) - { - if (isBool) - { - // Umbraco stored true as 1, which means it can be bool.tryParse'd - return m.Properties[propertyAlias].Value.ToString().Replace("1", "true").Replace("0", "false"); - } - return m.Properties[propertyAlias].Value.ToString(); - } - } - - 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; - if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) - { - if (m != null) - { - var approveStatus = GetMemberProperty(m, ApprovedPropertyTypeAlias, true); - if (string.IsNullOrEmpty(approveStatus) == false) - { - //try parsing as bool first (just in case) - if (bool.TryParse(approveStatus, out isApproved) == false) - { - int intStatus; - //if that fails, try parsing as int (since its normally stored as 0 or 1) - if (int.TryParse(approveStatus, out intStatus)) - { - isApproved = intStatus != 0; - } - } - } - else - { - //There is no property so we shouldn't use the approve status - isApproved = true; - } - } - } - else { - // if we don't use approve statuses - isApproved = true; - } - return isApproved; - } - #endregion - - #region Helper Methods - - /// - /// Checks the password. - /// - /// The password. - /// The dbPassword. - /// - internal bool CheckPassword(string password, string dbPassword) - { - string pass1 = password; - string pass2 = dbPassword; - - switch (PasswordFormat) - { - case MembershipPasswordFormat.Encrypted: - pass2 = DecodePassword(dbPassword); - break; - case MembershipPasswordFormat.Hashed: - pass1 = EncryptOrHashExistingPassword(password); - break; - default: - break; - } - return (pass1 == pass2) ? true : false; - } - - - /// - /// Encodes the password. - /// - /// The password. - /// The encoded password. - [Obsolete("Do not use this, it is the legacy way to encode a password")] - public string EncodePassword(string password) - { - return LegacyEncodePassword(password); - } - - /// - /// Unencode password. - /// - /// The encoded password. - /// The unencoded password. - [Obsolete("Do not use this, it is the legacy way to decode a password")] - public string UnEncodePassword(string encodedPassword) - { - return LegacyUnEncodePassword(encodedPassword); - } - - /// - /// Converts to membership user. - /// - /// The m. - /// - private MembershipUser ConvertToMembershipUser(Member m) - { - if (m == null) return null; - - var lastLogin = DateTime.Now; - var lastLocked = DateTime.MinValue; - var isApproved = true; - var isLocked = false; - var comment = ""; - var passwordQuestion = ""; - - // last login - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); - } - // approved - if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); - } - // locked - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); - } - // last locked - if (string.IsNullOrEmpty(_lastLockedOutPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLockedOutPropertyTypeAlias, false), out lastLocked); - } - // comment - if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) - { - comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); - } - // password question - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) - { - passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); - } - - return new MembershipUser(_providerName, m.LoginName, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDateTime, lastLogin, - DateTime.Now, DateTime.Now, lastLocked); - } - - /// - /// Converts to membership user. - /// - /// The m. - /// - private MembershipUser ConvertToMembershipUser(IMember m) - { - if (m == null) return null; - - var lastLogin = DateTime.Now; - var lastLocked = DateTime.MinValue; - var isApproved = true; - var isLocked = false; - var comment = ""; - var passwordQuestion = ""; - - // last login - if (string.IsNullOrEmpty(_lastLoginPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLoginPropertyTypeAlias, false), out lastLogin); - } - // approved - if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); - } - // locked - if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) - { - bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); - } - // last locked - if (string.IsNullOrEmpty(_lastLockedOutPropertyTypeAlias) == false) - { - DateTime.TryParse(GetMemberProperty(m, _lastLockedOutPropertyTypeAlias, false), out lastLocked); - } - // comment - if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) - { - comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); - } - // password question - if (string.IsNullOrEmpty(_passwordRetrievalQuestionPropertyTypeAlias) == false) - { - passwordQuestion = GetMemberProperty(m, _passwordRetrievalQuestionPropertyTypeAlias, false); - } - - return new MembershipUser(_providerName, m.Username, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDate, lastLogin, - DateTime.Now, DateTime.Now, lastLocked); - } - - #endregion - } -} +#region namespace +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Security; +using System.Configuration; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Security; +using umbraco.BusinessLogic; +using System.Security.Cryptography; +using System.Web.Util; +using System.Collections.Specialized; +using System.Configuration.Provider; +using System.Security; +using System.Security.Permissions; +using System.Runtime.CompilerServices; +using Member = umbraco.cms.businesslogic.member.Member; +using MemberType = umbraco.cms.businesslogic.member.MemberType; + +#endregion + +namespace umbraco.providers.members +{ + /// + /// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS) + /// + + public class UmbracoMembershipProvider : UmbracoMembershipProviderBase + { + #region Fields + + private string _providerName = Member.UmbracoMemberProviderName; + + #endregion + + /// + /// Override to maintain backwards compatibility with 0 required non-alphanumeric chars + /// + public override int DefaultMinNonAlphanumericChars + { + get { return 0; } + } + + /// + /// Override to maintain backwards compatibility with only 4 required length + /// + public override int DefaultMinPasswordLength + { + get { return 4; } + } + + /// + /// Override to maintain backwards compatibility + /// + public override bool DefaultUseLegacyEncoding + { + get { return true; } + } + + /// + /// For backwards compatibility, this provider supports this option + /// + public override bool AllowManuallyChangingPassword + { + get { return true; } + } + + #region Initialization Method + /// + /// Initializes the provider. + /// + /// The friendly name of the provider. + /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. + /// The name of the provider is null. + /// An attempt is made to call + /// on a provider after the provider + /// has already been initialized. + /// The name of the provider has a length of zero. + public override void Initialize(string name, NameValueCollection config) + { + // Intialize values from web.config + if (config == null) throw new ArgumentNullException("config"); + + if (string.IsNullOrEmpty(name)) name = "UmbracoMembershipProvider"; + + base.Initialize(name, config); + + _providerName = name; + + // test for membertype (if not specified, choose the first member type available) + if (config["defaultMemberTypeAlias"] != null) + DefaultMemberTypeAlias = config["defaultMemberTypeAlias"]; + else if (MemberType.GetAll.Length == 1) + 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"]; + } + + } + #endregion + + #region Methods + + /// + /// Processes a request to update the password for a membership user. + /// + /// The user to update the password for. + /// This property is ignore for this provider + /// The new password for the specified user. + /// + /// true if the password was updated successfully; otherwise, false. + /// + protected override bool PerformChangePassword(string username, string oldPassword, string newPassword) + { + //NOTE: due to backwards compatibilty reasons, this provider doesn't care about the old password and + // allows simply setting the password manually so we don't really care about the old password. + // This is allowed based on the overridden AllowManuallyChangingPassword option. + + // 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( + FormatPasswordForStorage(encodedPassword, salt)); + + UpdateMemberProperty(m, LastPasswordChangedPropertyTypeAlias, DateTime.Now); + + m.Save(); + + return true; + } + + /// + /// Processes a request to update the password question and answer for a membership user. + /// + /// The user to change the password question and answer for. + /// The password for the specified user. + /// The new password question for the specified user. + /// The new password answer for the specified user. + /// + /// true if the password question and answer are updated successfully; otherwise, false. + /// + protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) + { + var m = Member.GetMemberFromLoginName(username); + if (m == null) + { + return false; + } + + UpdateMemberProperty(m, PasswordRetrievalQuestionPropertyTypeAlias, newPasswordQuestion); + UpdateMemberProperty(m, PasswordRetrievalAnswerPropertyTypeAlias, newPasswordAnswer); + m.Save(); + return true; + } + + /// + /// Adds a new membership user to the data source. + /// + /// + /// The user name for the new user. + /// The password for the new user. + /// The e-mail address for the new user. + /// The password question for the new user. + /// The password answer for the new user + /// Whether or not the new user is approved to be validated. + /// The unique identifier from the membership data source for the user. + /// A enumeration value indicating whether the user was created successfully. + /// + /// A object populated with the information for the newly created user. + /// + protected override MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, + string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) + { + if (Member.GetMemberFromLoginName(username) != null) + { + status = MembershipCreateStatus.DuplicateUserName; + LogHelper.Warn("Cannot create member as username already exists: " + username); + return null; + } + + if (Member.GetMemberFromEmail(email) != null && RequiresUniqueEmail) + { + status = MembershipCreateStatus.DuplicateEmail; + LogHelper.Warn( + "Cannot create member as a member with the same email address exists: " + email); + return null; + } + + var memberType = MemberType.GetByAlias(memberTypeAlias); + if (memberType == null) + { + throw new InvalidOperationException("Could not find a member type with alias " + memberTypeAlias + ". Ensure your membership provider configuration is up to date and that the default member type exists."); + } + + var m = Member.MakeNew(username, email, memberType, User.GetUser(0)); + + string salt; + var encodedPassword = EncryptOrHashNewPassword(password, out salt); + + //set the password on the member + m.ChangePassword(FormatPasswordForStorage(encodedPassword, salt)); + + // custom fields + if (string.IsNullOrEmpty(PasswordRetrievalQuestionPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, PasswordRetrievalQuestionPropertyTypeAlias, passwordQuestion); + } + + if (string.IsNullOrEmpty(PasswordRetrievalAnswerPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, PasswordRetrievalAnswerPropertyTypeAlias, passwordAnswer); + } + + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, ApprovedPropertyTypeAlias, isApproved ? 1 : 0); + } + + if (string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); + } + + if (string.IsNullOrEmpty(LastPasswordChangedPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, LastPasswordChangedPropertyTypeAlias, DateTime.Now); + } + + var mUser = ConvertToMembershipUser(m); + + // save + m.Save(); + + status = MembershipCreateStatus.Success; + + return mUser; + } + + + /// + /// Removes a user from the membership data source. + /// + /// The name of the user to delete. + /// + /// TODO: This setting currently has no effect + /// + /// + /// true if the user was successfully deleted; otherwise, false. + /// + public override bool DeleteUser(string username, bool deleteAllRelatedData) + { + var m = Member.GetMemberFromLoginName(username); + if (m == null) return false; + m.delete(); + return true; + } + + /// + /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match. + /// + /// The e-mail address to search for. + /// 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 FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) + { + var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByEmail(emailToMatch, StringPropertyMatchType.Wildcard).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) + { + collection.Add(ConvertToMembershipUser(m)); + } + return collection; + } + + /// + /// Gets a collection of membership users where the user name contains the specified user name to match. + /// + /// The user name to search for. + /// 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 FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + { + var byEmail = ApplicationContext.Current.Services.MemberService.FindMembersByUsername(usernameToMatch, StringPropertyMatchType.Wildcard).ToArray(); + totalRecords = byEmail.Length; + var pagedResult = new PagedResult(totalRecords, pageIndex, pageSize); + + var collection = new MembershipUserCollection(); + foreach (var m in byEmail.Skip(pagedResult.SkipSize).Take(pageSize)) + { + collection.Add(ConvertToMembershipUser(m)); + } + 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 counter = 0; + var startIndex = pageSize * pageIndex; + var endIndex = startIndex + pageSize - 1; + var membersList = new MembershipUserCollection(); + var memberArray = Member.GetAll; + totalRecords = memberArray.Length; + + foreach (var m in memberArray) + { + if (counter >= startIndex) + membersList.Add(ConvertToMembershipUser(m)); + if (counter >= endIndex) break; + counter++; + } + return membersList; + + } + + /// + /// Gets the number of users currently accessing the application. + /// + /// + /// The number of users currently accessing the application. + /// + public override int GetNumberOfUsersOnline() + { + return Member.CachedMembers().Count; + } + + /// + /// Gets the password for the specified user name from the data source. + /// + /// The user to retrieve the password for. + /// The password answer for the user. + /// + /// 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 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(); + } + + /// + /// Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user. + /// + /// The name of the 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(string username, bool userIsOnline) + { + if (String.IsNullOrEmpty(username)) + return null; + Member m = Member.GetMemberFromLoginName(username); + if (m == null) return null; + else return ConvertToMembershipUser(m); + } + + /// + /// 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 asGuid = providerUserKey.TryConvertTo(); + if (asGuid.Success) + { + var m = new Member(asGuid.Result); + return ConvertToMembershipUser(m); + } + var asInt = providerUserKey.TryConvertTo(); + if (asInt.Success) + { + var m = new Member(asInt.Result); + return ConvertToMembershipUser(m); + } + throw new InvalidOperationException("The " + GetType() + " provider only supports GUID or Int as a providerUserKey"); + + } + + + /// + /// Gets the user name associated with the specified e-mail address. + /// + /// The e-mail address to search for. + /// + /// The user name associated with the specified e-mail address. If no match is found, return null. + /// + public override string GetUserNameByEmail(string email) + { + Member m = Member.GetMemberFromEmail(email); + return m == null ? null : m.LoginName; + } + + /// + /// 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) + { + 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) + //{ + // UpdateFailureCount(username, "passwordAnswer"); + + // 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"); + + 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"); + } + } + + string salt; + var encodedPassword = EncryptOrHashNewPassword(newPassword, out salt); + //set the password on the member + m.ChangePassword( + FormatPasswordForStorage(encodedPassword, salt)); + + if (string.IsNullOrEmpty(LastPasswordChangedPropertyTypeAlias) == false) + { + UpdateMemberProperty(m, LastPasswordChangedPropertyTypeAlias, DateTime.Now); + } + + m.Save(); + + return newPassword; + } + + /// + /// 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) + { + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + var m = Member.GetMemberFromLoginName(userName); + if (m != null) + { + UpdateMemberProperty(m, LockPropertyTypeAlias, 0); + m.Save(); + return true; + } + throw new Exception(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."); + } + + /// + /// Updates e-mail and potentially 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 = Member.GetMemberFromLoginName(user.UserName); + m.Email = user.Email; + + // if supported, update approve status + UpdateMemberProperty(m, ApprovedPropertyTypeAlias, user.IsApproved ? 1 : 0); + + // if supported, update lock status + UpdateMemberProperty(m, LockPropertyTypeAlias, user.IsLockedOut ? 1 : 0); + if (user.IsLockedOut) + { + UpdateMemberProperty(m, LastLockedOutPropertyTypeAlias, DateTime.Now); + } + + // if supported, update comment + UpdateMemberProperty(m, CommentPropertyTypeAlias, user.Comment); + + m.Save(); + } + + private static void UpdateMemberProperty(Member m, string propertyTypeAlias, object propertyValue) + { + if (string.IsNullOrEmpty(propertyTypeAlias) == false) + { + if (m.getProperty(propertyTypeAlias) != null) + { + m.getProperty(propertyTypeAlias).Value = propertyValue; + } + } + } + + private static string GetMemberProperty(Member m, string propertyTypeAlias, bool isBool) + { + if (string.IsNullOrEmpty(propertyTypeAlias) == false) + { + if (m.getProperty(propertyTypeAlias) != null && + m.getProperty(propertyTypeAlias).Value != null) + { + if (isBool) + { + // Umbraco stored true as 1, which means it can be bool.tryParse'd + return m.getProperty(propertyTypeAlias).Value.ToString().Replace("1", "true").Replace("0", "false"); + } + return m.getProperty(propertyTypeAlias).Value.ToString(); + } + } + + return null; + } + + private static string GetMemberProperty(IMember m, string propertyTypeAlias, bool isBool) + { + if (string.IsNullOrEmpty(propertyTypeAlias) == false) + { + if (m.Properties[propertyTypeAlias] != null && + m.Properties[propertyTypeAlias].Value != null) + { + if (isBool) + { + // Umbraco stored true as 1, which means it can be bool.tryParse'd + return m.Properties[propertyTypeAlias].Value.ToString().Replace("1", "true").Replace("0", "false"); + } + return m.Properties[propertyTypeAlias].Value.ToString(); + } + } + + 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; + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + if (m != null) + { + var approveStatus = GetMemberProperty(m, ApprovedPropertyTypeAlias, true); + if (string.IsNullOrEmpty(approveStatus) == false) + { + //try parsing as bool first (just in case) + if (bool.TryParse(approveStatus, out isApproved) == false) + { + int intStatus; + //if that fails, try parsing as int (since its normally stored as 0 or 1) + if (int.TryParse(approveStatus, out intStatus)) + { + isApproved = intStatus != 0; + } + } + } + else + { + //There is no property so we shouldn't use the approve status + isApproved = true; + } + } + } + else { + // if we don't use approve statuses + isApproved = true; + } + return isApproved; + } + #endregion + + #region Helper Methods + + /// + /// Checks the password. + /// + /// The password. + /// The dbPassword. + /// + internal bool CheckPassword(string password, string dbPassword) + { + string pass1 = password; + string pass2 = dbPassword; + + switch (PasswordFormat) + { + case MembershipPasswordFormat.Encrypted: + pass2 = DecodePassword(dbPassword); + break; + case MembershipPasswordFormat.Hashed: + pass1 = EncryptOrHashExistingPassword(password); + break; + default: + break; + } + return (pass1 == pass2) ? true : false; + } + + + /// + /// Encodes the password. + /// + /// The password. + /// The encoded password. + [Obsolete("Do not use this, it is the legacy way to encode a password")] + public string EncodePassword(string password) + { + return LegacyEncodePassword(password); + } + + /// + /// Unencode password. + /// + /// The encoded password. + /// The unencoded password. + [Obsolete("Do not use this, it is the legacy way to decode a password")] + public string UnEncodePassword(string encodedPassword) + { + return LegacyUnEncodePassword(encodedPassword); + } + + /// + /// Converts to membership user. + /// + /// The m. + /// + private MembershipUser ConvertToMembershipUser(Member m) + { + if (m == null) return null; + + var lastLogin = DateTime.Now; + var lastLocked = DateTime.MinValue; + var isApproved = true; + var isLocked = false; + var comment = ""; + var passwordQuestion = ""; + + // last login + if (string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, LastLoginPropertyTypeAlias, false), out lastLogin); + } + // approved + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); + } + // locked + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); + } + // last locked + if (string.IsNullOrEmpty(LastLockedOutPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, LastLockedOutPropertyTypeAlias, false), out lastLocked); + } + // comment + if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) + { + comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); + } + // password question + if (string.IsNullOrEmpty(PasswordRetrievalQuestionPropertyTypeAlias) == false) + { + passwordQuestion = GetMemberProperty(m, PasswordRetrievalQuestionPropertyTypeAlias, false); + } + + return new MembershipUser(_providerName, m.LoginName, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDateTime, lastLogin, + DateTime.Now, DateTime.Now, lastLocked); + } + + /// + /// Converts to membership user. + /// + /// The m. + /// + private MembershipUser ConvertToMembershipUser(IMember m) + { + if (m == null) return null; + + var lastLogin = DateTime.Now; + var lastLocked = DateTime.MinValue; + var isApproved = true; + var isLocked = false; + var comment = ""; + var passwordQuestion = ""; + + // last login + if (string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, LastLoginPropertyTypeAlias, false), out lastLogin); + } + // approved + if (string.IsNullOrEmpty(ApprovedPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, ApprovedPropertyTypeAlias, true), out isApproved); + } + // locked + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false) + { + bool.TryParse(GetMemberProperty(m, LockPropertyTypeAlias, true), out isLocked); + } + // last locked + if (string.IsNullOrEmpty(LastLockedOutPropertyTypeAlias) == false) + { + DateTime.TryParse(GetMemberProperty(m, LastLockedOutPropertyTypeAlias, false), out lastLocked); + } + // comment + if (string.IsNullOrEmpty(CommentPropertyTypeAlias) == false) + { + comment = GetMemberProperty(m, CommentPropertyTypeAlias, false); + } + // password question + if (string.IsNullOrEmpty(PasswordRetrievalQuestionPropertyTypeAlias) == false) + { + passwordQuestion = GetMemberProperty(m, PasswordRetrievalQuestionPropertyTypeAlias, false); + } + + return new MembershipUser(_providerName, m.Username, m.Id, m.Email, passwordQuestion, comment, isApproved, isLocked, m.CreateDate, lastLogin, + DateTime.Now, DateTime.Now, lastLocked); + } + + #endregion + } +} diff --git a/src/umbraco.providers/members/MembersProfileProvider.cs b/src/umbraco.providers/members/UmbracoProfileProvider.cs similarity index 69% rename from src/umbraco.providers/members/MembersProfileProvider.cs rename to src/umbraco.providers/members/UmbracoProfileProvider.cs index bd8f6a5ce7..4d1ac06ea3 100644 --- a/src/umbraco.providers/members/MembersProfileProvider.cs +++ b/src/umbraco.providers/members/UmbracoProfileProvider.cs @@ -1,159 +1,187 @@ -#region namespace -using System; -using System.Collections.Generic; -using System.Text; -using System.Web.Security; -using System.Configuration; -using umbraco.BusinessLogic; -using System.Security.Cryptography; -using System.Web.Util; -using System.Collections.Specialized; -using System.Configuration.Provider; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.member; -using System.Collections; -using System.Web.Profile; -#endregion - -namespace umbraco.providers.members { - public class UmbracoProfileProvider : ProfileProvider { - - private string m_ApplicationName = ""; - - public override string ApplicationName { - get { - return m_ApplicationName; - } - set { - m_ApplicationName = value; - } - } - public override string Description { - get { - return "Profile Provider for umbraco member profile data"; - } - } - public override string Name { - get { - return base.Name; - } - } - public override void Initialize(string name, NameValueCollection config) { - - if (config == null) - throw new ArgumentNullException("Null configuration parameters"); - - if (String.IsNullOrEmpty(name)) - name = "UmbracoProfileProvider"; - - base.Initialize(name, config); - - m_ApplicationName = config["applicationName"]; - if (String.IsNullOrEmpty(m_ApplicationName)) - m_ApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath; - config.Remove("applicationName"); - - // if the config element contains unused parameters we should throw an exception - if (config.Count > 0) { - string attrib = config.GetKey(0); - if (!String.IsNullOrEmpty(attrib)) - throw new ProviderException(String.Format("Unrecognized attribute: {0}", attrib)); - } - - - } - - public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) { - throw new NotSupportedException(); - } - - public override int DeleteProfiles(string[] usernames) { - throw new NotSupportedException(); - } - - public override int DeleteProfiles(ProfileInfoCollection profiles) { - throw new NotSupportedException(); - } - - public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) { - throw new NotSupportedException(); - } - - public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { - throw new NotSupportedException(); - } - - public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) { - throw new NotSupportedException(); - } - public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords) { - throw new NotSupportedException(); - } - public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) { - throw new NotSupportedException(); - } - /// - /// Returns the collection of settings property values for the current umbraco member. - /// - /// A describing the current application use. - /// A containing the settings property group whose values are to be retrieved. - /// - /// A containing the values for the specified settings property group. - /// - public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) { - SettingsPropertyValueCollection settings = new SettingsPropertyValueCollection(); - - if (collection.Count == 0) - return settings; - - foreach(SettingsProperty property in collection){ - SettingsPropertyValue pv = new SettingsPropertyValue(property); - settings.Add(pv); - } - - // get the current user - string username = (string)context["UserName"]; - Member m = Member.GetMemberFromLoginName(username); - if (m == null) - throw new ProviderException(String.Format("No member with username '{0}' exists", username)); - - foreach (SettingsPropertyValue spv in settings) { - if (m.getProperty(spv.Name) != null) { - spv.Deserialized = true; - spv.PropertyValue = m.getProperty(spv.Name).Value; - } - } - - return settings; - - } - - /// - /// Sets the values of the specified group of property settings for the current umbraco member. - /// - /// A describing the current application usage. - /// A representing the group of property settings to set. - public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) { - - string username = (string)context["UserName"]; - bool authenticated = (bool)context["IsAuthenticated"]; - - if (String.IsNullOrEmpty(username) || collection.Count == 0) - return; - - Member m = Member.GetMemberFromLoginName(username); - if (m == null) - throw new ProviderException(String.Format("No member with username '{0}' exists", username)); - - - foreach (SettingsPropertyValue spv in collection) { - if (!authenticated && !(bool)spv.Property.Attributes["AllowAnonymous"]) - continue; - - if (m.getProperty(spv.Name) != null) - m.getProperty(spv.Name).Value = spv.PropertyValue; - } - m.Save(); - } - } -} +#region namespace +using System; +using System.Collections.Generic; +using System.Text; +using System.Web.Security; +using System.Configuration; +using umbraco.BusinessLogic; +using System.Security.Cryptography; +using System.Web.Util; +using System.Collections.Specialized; +using System.Configuration.Provider; +using umbraco.cms.businesslogic; +using umbraco.cms.businesslogic.member; +using System.Collections; +using System.Web.Profile; +#endregion + +namespace umbraco.providers.members +{ + public class UmbracoProfileProvider : ProfileProvider + { + + private string _applicationName = ""; + + public override string ApplicationName + { + get + { + return _applicationName; + } + set + { + _applicationName = value; + } + } + public override string Description + { + get + { + return "Profile Provider for umbraco member profile data"; + } + } + public override string Name + { + get + { + return base.Name; + } + } + public override void Initialize(string name, NameValueCollection config) + { + + if (config == null) + throw new ArgumentNullException("Null configuration parameters"); + + if (String.IsNullOrEmpty(name)) + name = "UmbracoProfileProvider"; + + base.Initialize(name, config); + + _applicationName = config["applicationName"]; + if (String.IsNullOrEmpty(_applicationName)) + _applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath; + config.Remove("applicationName"); + + // if the config element contains unused parameters we should throw an exception + if (config.Count > 0) + { + var attrib = config.GetKey(0); + if (string.IsNullOrEmpty(attrib) == false) + { + throw new ProviderException(String.Format("Unrecognized attribute: {0}", attrib)); + } + + } + + + } + + public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) + { + throw new NotSupportedException(); + } + + public override int DeleteProfiles(string[] usernames) + { + throw new NotSupportedException(); + } + + public override int DeleteProfiles(ProfileInfoCollection profiles) + { + throw new NotSupportedException(); + } + + public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + + public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + + public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords) + { + throw new NotSupportedException(); + } + public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) + { + throw new NotSupportedException(); + } + /// + /// Returns the collection of settings property values for the current umbraco member. + /// + /// A describing the current application use. + /// A containing the settings property group whose values are to be retrieved. + /// + /// A containing the values for the specified settings property group. + /// + public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) + { + var settings = new SettingsPropertyValueCollection(); + + if (collection.Count == 0) + return settings; + + foreach (SettingsProperty property in collection) + { + var pv = new SettingsPropertyValue(property); + settings.Add(pv); + } + + // get the current user + var username = (string)context["UserName"]; + var m = Member.GetMemberFromLoginName(username); + if (m == null) + throw new ProviderException(String.Format("No member with username '{0}' exists", username)); + + foreach (SettingsPropertyValue spv in settings) + { + if (m.getProperty(spv.Name) != null) + { + spv.Deserialized = true; + spv.PropertyValue = m.getProperty(spv.Name).Value; + } + } + + return settings; + + } + + /// + /// Sets the values of the specified group of property settings for the current umbraco member. + /// + /// A describing the current application usage. + /// A representing the group of property settings to set. + public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) + { + + var username = (string)context["UserName"]; + var authenticated = (bool)context["IsAuthenticated"]; + + if (string.IsNullOrEmpty(username) || collection.Count == 0) + return; + + var m = Member.GetMemberFromLoginName(username); + if (m == null) + throw new ProviderException(String.Format("No member with username '{0}' exists", username)); + + + foreach (SettingsPropertyValue spv in collection) + { + if (authenticated == false && (bool)spv.Property.Attributes["AllowAnonymous"] == false) + continue; + + if (m.getProperty(spv.Name) != null) + m.getProperty(spv.Name).Value = spv.PropertyValue; + } + m.Save(); + } + } +} diff --git a/src/umbraco.providers/members/MembersRoleProvider.cs b/src/umbraco.providers/members/UmbracoRoleProvider.cs similarity index 97% rename from src/umbraco.providers/members/MembersRoleProvider.cs rename to src/umbraco.providers/members/UmbracoRoleProvider.cs index 41a6da6568..bbae8aabfa 100644 --- a/src/umbraco.providers/members/MembersRoleProvider.cs +++ b/src/umbraco.providers/members/UmbracoRoleProvider.cs @@ -1,253 +1,256 @@ -#region namespace -using System; -using System.Collections.Generic; -using System.Text; -using System.Web.Security; -using System.Configuration; -using umbraco.BusinessLogic; -using System.Security.Cryptography; -using System.Web.Util; -using System.Collections.Specialized; -using System.Configuration.Provider; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.member; -using System.Collections; -#endregion - -namespace umbraco.providers.members { - public class UmbracoRoleProvider : RoleProvider { - #region - private string _ApplicationName = Member.UmbracoRoleProviderName; - #endregion - - #region Properties - /// - /// Gets or sets the name of the application to store and retrieve role information for. - /// - /// - /// The name of the application to store and retrieve role information for. - public override string ApplicationName { - get { - return _ApplicationName; - } - set { - if (string.IsNullOrEmpty(value)) - throw new ProviderException("ApplicationName cannot be empty."); - - if (value.Length > 0x100) - throw new ProviderException("Provider application name too long."); - - _ApplicationName = value; - } - } - #endregion - - #region Initialization Method - /// - /// Initializes the provider. - /// - /// The friendly name of the provider. - /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. - /// The name of the provider is null. - /// An attempt is made to call - /// on a provider - /// after the provider has already been initialized. - /// The name of the provider has a length of zero. - public override void Initialize(string name, NameValueCollection config) { - // Initialize values from web.config - if (config == null) throw new ArgumentNullException("config"); - - if (name == null || name.Length == 0) name = "UmbracoMemberRoleProvider"; - - if (String.IsNullOrEmpty(config["description"])) { - config.Remove("description"); - config.Add("description", "Umbraco Member Role provider"); - } - - // Initialize the abstract base class. - base.Initialize(name, config); - - this._ApplicationName = config["applicationName"]; - if (string.IsNullOrEmpty(this._ApplicationName)) - this._ApplicationName = SecUtility.GetDefaultAppName(); - } - #endregion - - #region Methods - /// - /// Adds the specified user names to the specified roles for the configured applicationName. - /// - /// A string array of user names to be added to the specified roles. - /// A string array of the role names to add the specified user names to. - public override void AddUsersToRoles(string[] usernames, string[] roleNames) { - ArrayList roles = new ArrayList(); - foreach (string role in roleNames) - try { - roles.Add(MemberGroup.GetByName(role).Id); - } catch { - throw new ProviderException(String.Format("No role with name '{0}' exists", role)); - } - foreach (string username in usernames) { - Member m = Member.GetMemberFromLoginName(username); - foreach (int roleId in roles) - m.AddGroup(roleId); - } - } - - /// - /// Adds a new role to the data source for the configured applicationName. - /// - /// The name of the role to create. - public override void CreateRole(string roleName) { - MemberGroup.MakeNew(roleName, User.GetUser(0)); - } - - /// - /// Removes a role from the data source for the configured applicationName. - /// - /// The name of the role to delete. - /// If true, throw an exception if roleName has one or more members and do not delete roleName. - /// - /// true if the role was successfully deleted; otherwise, false. - /// - public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { - MemberGroup group = MemberGroup.GetByName(roleName); - if (group == null) - throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); - else if (throwOnPopulatedRole && group.GetMembersAsIds().Length > 0) - throw new ProviderException(String.Format("Can't delete role '{0}', there are members assigned to the role", roleName)); - else { - foreach (Member m in group.GetMembers()) - m.RemoveGroup(group.Id); - group.delete(); - return true; - } - } - - /// - /// Gets an array of user names in a role where the user name contains the specified user name to match. - /// - /// The role to search in. - /// The user name to search for. - /// - /// A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. - /// - public override string[] FindUsersInRole(string roleName, string usernameToMatch) { - ArrayList members = new ArrayList(); - MemberGroup group = MemberGroup.GetByName(roleName); - if (group == null) - throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); - else { - foreach (Member m in group.GetMembers(usernameToMatch)) - members.Add(m.LoginName); - return (string[])members.ToArray(typeof(string)); - } - } - - /// - /// Gets a list of all the roles for the configured applicationName. - /// - /// - /// A string array containing the names of all the roles stored in the data source for the configured applicationName. - /// - public override string[] GetAllRoles() { - ArrayList roles = new ArrayList(); - foreach (MemberGroup mg in MemberGroup.GetAll) - roles.Add(mg.Text); - return (string[])roles.ToArray(typeof(string)); - } - - /// - /// Gets a list of the roles that a specified user is in for the configured applicationName. - /// - /// The user to return a list of roles for. - /// - /// A string array containing the names of all the roles that the specified user is in for the configured applicationName. - /// - public override string[] GetRolesForUser(string username) { - ArrayList roles = new ArrayList(); - Member m = Member.GetMemberFromLoginName(username); - if (m != null) { - IDictionaryEnumerator ide = m.Groups.GetEnumerator(); - while (ide.MoveNext()) - roles.Add(((MemberGroup)ide.Value).Text); - return (string[])roles.ToArray(typeof(string)); - } else - throw new ProviderException(String.Format("No member with username '{0}' exists", username)); - } - - /// - /// Gets a list of users in the specified role for the configured applicationName. - /// - /// The name of the role to get the list of users for. - /// - /// A string array containing the names of all the users who are members of the specified role for the configured applicationName. - /// - public override string[] GetUsersInRole(string roleName) { - ArrayList members = new ArrayList(); - MemberGroup group = MemberGroup.GetByName(roleName); - if (group == null) - throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); - else { - foreach (Member m in group.GetMembers()) - members.Add(m.LoginName); - return (string[])members.ToArray(typeof(string)); - } - } - - /// - /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName. - /// - /// The user name to search for. - /// The role to search in. - /// - /// true if the specified user is in the specified role for the configured applicationName; otherwise, false. - /// - public override bool IsUserInRole(string username, string roleName) { - Member m = Member.GetMemberFromLoginName(username); - if (m == null) - throw new ProviderException(String.Format("No user with name '{0}' exists", username)); - else { - MemberGroup mg = MemberGroup.GetByName(roleName); - if (mg == null) - throw new ProviderException(String.Format("No Membergroup with name '{0}' exists", roleName)); - else - return mg.HasMember(m.Id); - } - } - - /// - /// Removes the specified user names from the specified roles for the configured applicationName. - /// - /// A string array of user names to be removed from the specified roles. - /// A string array of role names to remove the specified user names from. - public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { - ArrayList roles = new ArrayList(); - foreach (string role in roleNames) - try { - roles.Add(MemberGroup.GetByName(role).Id); - } catch { - throw new ProviderException(String.Format("No role with name '{0}' exists", role)); - } - foreach (string username in usernames) { - Member m = Member.GetMemberFromLoginName(username); - foreach (int roleId in roles) - m.RemoveGroup(roleId); - } - } - - /// - /// Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. - /// - /// The name of the role to search for in the data source. - /// - /// true if the role name already exists in the data source for the configured applicationName; otherwise, false. - /// - public override bool RoleExists(string roleName) { - MemberGroup mg = MemberGroup.GetByName(roleName); - return mg != null; - } - #endregion - - } -} +#region namespace +using System; +using System.Collections.Generic; +using System.Text; +using System.Web.Security; +using System.Configuration; +using umbraco.BusinessLogic; +using System.Security.Cryptography; +using System.Web.Util; +using System.Collections.Specialized; +using System.Configuration.Provider; +using umbraco.cms.businesslogic; +using umbraco.cms.businesslogic.member; +using System.Collections; +#endregion + +namespace umbraco.providers.members +{ + public class UmbracoRoleProvider : RoleProvider + { + + #region + private string _ApplicationName = Member.UmbracoRoleProviderName; + #endregion + + #region Properties + /// + /// Gets or sets the name of the application to store and retrieve role information for. + /// + /// + /// The name of the application to store and retrieve role information for. + public override string ApplicationName { + get { + return _ApplicationName; + } + set { + if (string.IsNullOrEmpty(value)) + throw new ProviderException("ApplicationName cannot be empty."); + + if (value.Length > 0x100) + throw new ProviderException("Provider application name too long."); + + _ApplicationName = value; + } + } + #endregion + + #region Initialization Method + /// + /// Initializes the provider. + /// + /// The friendly name of the provider. + /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. + /// The name of the provider is null. + /// An attempt is made to call + /// on a provider + /// after the provider has already been initialized. + /// The name of the provider has a length of zero. + public override void Initialize(string name, NameValueCollection config) { + // Initialize values from web.config + if (config == null) throw new ArgumentNullException("config"); + + if (name == null || name.Length == 0) name = "UmbracoMemberRoleProvider"; + + if (String.IsNullOrEmpty(config["description"])) { + config.Remove("description"); + config.Add("description", "Umbraco Member Role provider"); + } + + // Initialize the abstract base class. + base.Initialize(name, config); + + this._ApplicationName = config["applicationName"]; + if (string.IsNullOrEmpty(this._ApplicationName)) + this._ApplicationName = SecUtility.GetDefaultAppName(); + } + #endregion + + #region Methods + /// + /// Adds the specified user names to the specified roles for the configured applicationName. + /// + /// A string array of user names to be added to the specified roles. + /// A string array of the role names to add the specified user names to. + public override void AddUsersToRoles(string[] usernames, string[] roleNames) { + ArrayList roles = new ArrayList(); + foreach (string role in roleNames) + try { + roles.Add(MemberGroup.GetByName(role).Id); + } catch { + throw new ProviderException(String.Format("No role with name '{0}' exists", role)); + } + foreach (string username in usernames) { + Member m = Member.GetMemberFromLoginName(username); + foreach (int roleId in roles) + m.AddGroup(roleId); + } + } + + /// + /// Adds a new role to the data source for the configured applicationName. + /// + /// The name of the role to create. + public override void CreateRole(string roleName) { + MemberGroup.MakeNew(roleName, User.GetUser(0)); + } + + /// + /// Removes a role from the data source for the configured applicationName. + /// + /// The name of the role to delete. + /// If true, throw an exception if roleName has one or more members and do not delete roleName. + /// + /// true if the role was successfully deleted; otherwise, false. + /// + public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { + MemberGroup group = MemberGroup.GetByName(roleName); + if (group == null) + throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); + else if (throwOnPopulatedRole && group.GetMembersAsIds().Length > 0) + throw new ProviderException(String.Format("Can't delete role '{0}', there are members assigned to the role", roleName)); + else { + foreach (Member m in group.GetMembers()) + m.RemoveGroup(group.Id); + group.delete(); + return true; + } + } + + /// + /// Gets an array of user names in a role where the user name contains the specified user name to match. + /// + /// The role to search in. + /// The user name to search for. + /// + /// A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role. + /// + public override string[] FindUsersInRole(string roleName, string usernameToMatch) { + ArrayList members = new ArrayList(); + MemberGroup group = MemberGroup.GetByName(roleName); + if (group == null) + throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); + else { + foreach (Member m in group.GetMembers(usernameToMatch)) + members.Add(m.LoginName); + return (string[])members.ToArray(typeof(string)); + } + } + + /// + /// Gets a list of all the roles for the configured applicationName. + /// + /// + /// A string array containing the names of all the roles stored in the data source for the configured applicationName. + /// + public override string[] GetAllRoles() { + ArrayList roles = new ArrayList(); + foreach (MemberGroup mg in MemberGroup.GetAll) + roles.Add(mg.Text); + return (string[])roles.ToArray(typeof(string)); + } + + /// + /// Gets a list of the roles that a specified user is in for the configured applicationName. + /// + /// The user to return a list of roles for. + /// + /// A string array containing the names of all the roles that the specified user is in for the configured applicationName. + /// + public override string[] GetRolesForUser(string username) { + ArrayList roles = new ArrayList(); + Member m = Member.GetMemberFromLoginName(username); + if (m != null) { + IDictionaryEnumerator ide = m.Groups.GetEnumerator(); + while (ide.MoveNext()) + roles.Add(((MemberGroup)ide.Value).Text); + return (string[])roles.ToArray(typeof(string)); + } else + throw new ProviderException(String.Format("No member with username '{0}' exists", username)); + } + + /// + /// Gets a list of users in the specified role for the configured applicationName. + /// + /// The name of the role to get the list of users for. + /// + /// A string array containing the names of all the users who are members of the specified role for the configured applicationName. + /// + public override string[] GetUsersInRole(string roleName) { + ArrayList members = new ArrayList(); + MemberGroup group = MemberGroup.GetByName(roleName); + if (group == null) + throw new ProviderException(String.Format("No role with name '{0}' exists", roleName)); + else { + foreach (Member m in group.GetMembers()) + members.Add(m.LoginName); + return (string[])members.ToArray(typeof(string)); + } + } + + /// + /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName. + /// + /// The user name to search for. + /// The role to search in. + /// + /// true if the specified user is in the specified role for the configured applicationName; otherwise, false. + /// + public override bool IsUserInRole(string username, string roleName) { + Member m = Member.GetMemberFromLoginName(username); + if (m == null) + throw new ProviderException(String.Format("No user with name '{0}' exists", username)); + else { + MemberGroup mg = MemberGroup.GetByName(roleName); + if (mg == null) + throw new ProviderException(String.Format("No Membergroup with name '{0}' exists", roleName)); + else + return mg.HasMember(m.Id); + } + } + + /// + /// Removes the specified user names from the specified roles for the configured applicationName. + /// + /// A string array of user names to be removed from the specified roles. + /// A string array of role names to remove the specified user names from. + public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { + ArrayList roles = new ArrayList(); + foreach (string role in roleNames) + try { + roles.Add(MemberGroup.GetByName(role).Id); + } catch { + throw new ProviderException(String.Format("No role with name '{0}' exists", role)); + } + foreach (string username in usernames) { + Member m = Member.GetMemberFromLoginName(username); + foreach (int roleId in roles) + m.RemoveGroup(roleId); + } + } + + /// + /// Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName. + /// + /// The name of the role to search for in the data source. + /// + /// true if the role name already exists in the data source for the configured applicationName; otherwise, false. + /// + public override bool RoleExists(string roleName) { + MemberGroup mg = MemberGroup.GetByName(roleName); + return mg != null; + } + #endregion + + } +} diff --git a/src/umbraco.providers/umbraco.providers.csproj b/src/umbraco.providers/umbraco.providers.csproj index de33276993..e7e5e28b12 100644 --- a/src/umbraco.providers/umbraco.providers.csproj +++ b/src/umbraco.providers/umbraco.providers.csproj @@ -80,9 +80,9 @@ - - - + + +