I have found solution to this problem myself :)
The reason of permanent error is in method "ChangePassword" of class "YafMembershipProvider"
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
string salt = string.Empty;
string str2 = string.Empty;
if (!this.IsPasswordCompliant(newPassword))
{
return false;
}
UserPasswordInfo info = UserPasswordInfo.CreateInstanceFromDB(this.ApplicationName, username, false, this.UseSalt, this.HashHex, this.HashCase, this.HashRemoveChars, this.MSCompliant);
if (info == null)
{
return false;
}
if (!info.IsCorrectPassword(oldPassword))
{
return false;
}
//here begin
if (this.UseSalt)
{
salt = GenerateSalt();
}
//here end
str2 = EncodeString(newPassword, (int) this.PasswordFormat, salt, this.UseSalt, this.HashHex, this.HashCase, this.HashRemoveChars, this.MSCompliant);
DB.ChangePassword(this.ApplicationName, username, str2, salt, (int) this.PasswordFormat, info.PasswordAnswer);
return true;
}
Every time in changing user password we generate new value in field "PasswordSalt" but our password question was encoded with previous salt. That is reason why entered "PasswordAnswer" is not equal to saved one in database.
As we can see "DB.ChangePasswordvariable" method saves new value "salt" for current user but does not change encoded password question!!
public static void ChangePassword(string appName, string username, string newPassword, string newSalt, int passwordFormat, string newPasswordAnswer)
{
using (SqlCommand command = new SqlCommand(DBAccess.GetObjectName("prov_changepassword")))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@ApplicationName", appName);
command.Parameters.AddWithValue("@Username", username);
command.Parameters.AddWithValue("@Password", newPassword);
command.Parameters.AddWithValue("@PasswordSalt", newSalt);
command.Parameters.AddWithValue("@PasswordFormat", passwordFormat);
command.Parameters.AddWithValue("@PasswordAnswer", newPasswordAnswer);
DBAccess.ExecuteNonQuery(command);
}
}
One should input
if (this.UseSalt && String.IsNullOrEmpty(info.PasswordSalt))
{
salt = GenerateSalt();
}
else
{
salt = info.PasswordSalt;
}
instead of marked part (by 'begin here' and 'end here') in code "ChangePassword" method and recopile dll.