/// <summary>
        /// Adds a new membership user to the data source.
        /// </summary>
        /// <param name="username">The user name for the new user.</param>
        /// <param name="password">The password for the new user.</param>
        /// <param name="email">The e-mail address for the new user.</param>
        /// <param name="passwordQuestion">The password question for the new user.</param>
        /// <param name="passwordAnswer">The password answer for the new user</param>
        /// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
        /// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
        /// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"/> enumeration value indicating whether the user was created successfully.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Security.MembershipUser"/> object populated with the information for the newly created user.
        /// </returns>
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            #region Validation

            if (!SecUtility.ValidateParameter(ref username, true, true, InvalidUsernameCharacters, MAX_USERNAME_LENGTH))
            {
                status = MembershipCreateStatus.InvalidUserName;
                return null;
            }

            if (!SecUtility.ValidateParameter(ref email, this.RequiresUniqueEmail, this.RequiresUniqueEmail, InvalidEmailCharacters, MAX_EMAIL_LENGTH))
            {
                status = MembershipCreateStatus.InvalidEmail;
                return null;
            }

            if (!SecUtility.ValidateParameter(ref password, true, true, null, MAX_PASSWORD_LENGTH))
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (password.Length > MAX_PASSWORD_LENGTH)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (null != passwordAnswer)
            {
                passwordAnswer = passwordAnswer.Trim();
            }

            if (string.IsNullOrEmpty(passwordAnswer))
            {
                if (RequiresQuestionAndAnswer)
                {
                    status = MembershipCreateStatus.InvalidAnswer;
                    return null;
                }
            }
            else
            {
                if (passwordAnswer.Length > MAX_PASSWORD_ANSWER_LENGTH)
                {
                    status = MembershipCreateStatus.InvalidAnswer;
                    return null;
                }
            }

            if (!SecUtility.ValidateParameter(ref passwordQuestion, this.RequiresQuestionAndAnswer, true, null, MAX_PASSWORD_QUESTION_LENGTH))
            {
                status = MembershipCreateStatus.InvalidQuestion;
                return null;
            }

            if ((null != providerUserKey) && !(providerUserKey is Guid))
            {
                status = MembershipCreateStatus.InvalidProviderUserKey;
                return null;
            }

            if (password.Length < this.MinRequiredPasswordLength)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (this.MinRequiredNonAlphanumericCharacters > 0)
            {
                int numNonAlphaNumericChars = 0;
                for (int i = 0; i < password.Length; i++)
                {
                    if (!char.IsLetterOrDigit(password, i))
                    {
                        numNonAlphaNumericChars++;
                    }
                }

                if (numNonAlphaNumericChars < this.MinRequiredNonAlphanumericCharacters)
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }
            }

            if ((this.PasswordStrengthRegularExpression.Length > 0) && !Regex.IsMatch(password, this.PasswordStrengthRegularExpression))
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            #endregion

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);

            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && !String.IsNullOrEmpty(GetUserNameByEmail(email)))
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser u = GetUser(username, false);
            if (null != u)
            {
                status = MembershipCreateStatus.DuplicateUserName;
                return null;
            }

            DateTime createDate = DateTime.UtcNow;

            if (null == providerUserKey)
            {
                providerUserKey = Guid.NewGuid();
            }
            else
            {
                if (!(providerUserKey is Guid))
                {
                    status = MembershipCreateStatus.InvalidProviderUserKey;
                    return null;
                }
            }

            var createAt = DateTime.UtcNow;
            string salt = GenerateSalt();

            var answer = passwordAnswer;
            if (null != answer)
            {
                answer = EncodePassword(passwordAnswer.ToLowerInvariant(), PasswordFormat, salt);
            }

            var user = new User();
            user.Id = (Guid)providerUserKey;
            user.Username = username;
            user.LowercaseUsername = username.ToLowerInvariant();
            user.DisplayName = username;
            user.Email = email;
            user.LowercaseEmail = (null == email) ? null : email.ToLowerInvariant();
            user.Password = EncodePassword(password, PasswordFormat, salt);
            user.PasswordQuestion = passwordQuestion;
            user.PasswordAnswer = answer;
            user.PasswordFormat = PasswordFormat;
            user.PasswordSalt = salt;
            user.IsApproved = isApproved;
            user.LastPasswordChangedDate = DateTime.MinValue;
            user.CreateDate = createAt;
            user.IsLockedOut = false;
            user.LastLockedOutDate = DateTime.MinValue;
            user.LastActivityDate = createAt;
            user.FailedPasswordAnswerAttemptCount = 0;
            user.FailedPasswordAnswerAttemptWindowStart = DateTime.MinValue;
            user.FailedPasswordAttemptCount = 0;
            user.FailedPasswordAttemptWindowStart = DateTime.MinValue;

            var msg = String.Format("Error creating new User '{0}'", username);
            Save(user, msg, "CreateUser");

            status = MembershipCreateStatus.Success;
            return GetUser(username, false);
        }
        /// <summary>
        /// A helper method that performs the checks and updates associated User with password failure tracking
        /// </summary>
        /// <param name="username"></param>
        /// <param name="failureType"></param>
        /// <param name="isAuthenticated"></param>
        protected void UpdateFailureCount(User user, string failureType, bool isAuthenticated)
        {
            if (!((failureType == "password") || (failureType == "passwordAnswer")))
            {
                throw new ArgumentException("Invalid value for failureType parameter. Must be 'password' or 'passwordAnswer'.", "failureType");
            }

            if (user.IsLockedOut)
                return; // Just exit without updating any fields if user is locked out

            if (isAuthenticated)
            {
                // User is valid, so make sure Attempt Counts and IsLockedOut fields have been reset
                if ((user.FailedPasswordAttemptCount > 0) || (user.FailedPasswordAnswerAttemptCount > 0))
                {
                    user.FailedPasswordAnswerAttemptCount = 0;
                    user.FailedPasswordAttemptCount = 0;
                    user.FailedPasswordAnswerAttemptWindowStart = DateTime.MinValue;
                    user.FailedPasswordAttemptWindowStart = DateTime.MinValue;
                    var msg = String.Format("Unable to reset Authenticated User's FailedPasswordAttemptCount property for user '{0}'", user.Username);
                    Save(user, msg, "UpdateFailureCount");
                }
                return;
            }

            // If we get here that means isAuthenticated = false, which means the user did not log on successfully.
            // Log the failure and possibly lock out the user if she exceeded the number of allowed attempts.

            DateTime windowStart = DateTime.MinValue;
            int failureCount = 0;
            if (failureType == "password")
            {
                windowStart = user.FailedPasswordAttemptWindowStart;
                failureCount = user.FailedPasswordAttemptCount;
            }
            else if (failureType == "passwordAnswer")
            {
                windowStart = user.FailedPasswordAnswerAttemptWindowStart;
                failureCount = user.FailedPasswordAnswerAttemptCount;
            }

            DateTime windowEnd = windowStart.AddMinutes(PasswordAttemptWindow);

            if (failureCount == 0 || DateTime.UtcNow > windowEnd)
            {
                // First password failure or outside of PasswordAttemptWindow.
                // Start a new password failure count from 1 and a new window starting now.

                if (failureType == "password")
                {
                    user.FailedPasswordAttemptCount = 1;
                    user.FailedPasswordAttemptWindowStart = DateTime.UtcNow;
                }
                else if (failureType == "passwordAnswer")
                {
                    user.FailedPasswordAnswerAttemptCount = 1;
                    user.FailedPasswordAnswerAttemptWindowStart = DateTime.UtcNow;
                }

                var msg = String.Format("Unable to update failure count and window start for user '{0}'", user.Username);
                Save(user, msg, "UpdateFailureCount");

                return;
            }

            // within PasswordAttemptWindow

            failureCount++;

            if (failureCount >= MaxInvalidPasswordAttempts)
            {
                // Password attempts have exceeded the failure threshold. Lock out the user.
                user.IsLockedOut = true;
                user.LastLockedOutDate = DateTime.UtcNow;
                user.FailedPasswordAttemptCount = failureCount;

                var msg = String.Format("Unable to lock out user '{0}'", user.Username);
                Save(user, msg, "UpdateFailureCount");

                return;
            }

            // Password attempts have not exceeded the failure threshold. Update
            // the failure counts. Leave the window the same.

            if (failureType == "password")
            {
                user.FailedPasswordAttemptCount = failureCount;
            }
            else if (failureType == "passwordAnswer")
            {
                user.FailedPasswordAnswerAttemptCount = failureCount;
            }

            {
                var msg = String.Format("Unable to update failure count for user '{0}'", user.Username);
                Save(user, msg, "UpdateFailureCount");
            }

            return;
        }
 /// <summary>
 /// Saves a User to persistent storage
 /// </summary>
 /// <param name="user">The User to save</param>
 /// <param name="failureMessage">A message that will be used if an exception is raised during save</param>
 /// <param name="action">The name of the action which attempted the save (ex. "CreateUser"). Used in case exceptions are written to EventLog.</param>
 protected void Save(User user, string failureMessage, string action)
 {
     SafeModeResult result = null;
     try {
         var users = Collection;
         result = users.Save(user, SafeMode.True);
     }
     catch (Exception ex) {
         HandleDataExceptionAndThrow(new ProviderException(failureMessage, ex), action);
     }
     if (null == result)
     {
         HandleDataExceptionAndThrow(new ProviderException("Save to database did not return a status result"), action);
     }
     else if (!result.Ok)
     {
         HandleDataExceptionAndThrow(new ProviderException(result.LastErrorMessage), action);
     }
 }
        protected MembershipUser ToMembershipUser(User user)
        {
            if (null == user)
                return null;

            return new MembershipUser(this.Name, user.Username, user.Id, user.Email,
                user.PasswordQuestion, user.Comment, user.IsApproved, user.IsLockedOut,
                user.CreateDate, user.LastLoginDate, user.LastActivityDate, user.LastPasswordChangedDate,
                user.LastLockedOutDate
            );
        }
        protected bool CheckPassword(User user, string password, bool failIfNotApproved)
        {
            if (null == user) return false;
            if (!user.IsApproved && failIfNotApproved) return false;

            string encodedPwdFromUser = EncodePassword(password, user.PasswordFormat, user.PasswordSalt);
            bool isAuthenticated = user.Password.Equals(encodedPwdFromUser);

            if ((isAuthenticated && (user.FailedPasswordAttemptCount == 0)) && (user.FailedPasswordAnswerAttemptCount == 0))
                return true;

            UpdateFailureCount(user, "password", isAuthenticated);

            return isAuthenticated;
        }