/// <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; }