using System;
using System.Collections.Generic;
using System.Configuration;
using System.Configuration.Provider;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
using System.Web.Security;
using System.Xml.Linq;
using Neo.Afx.ComponentModel;
using Neo.Afx.Diagnostics;
namespace Neo.Afx.Security
{
///
/// Manages storage of membership information for an RIA domain service application in a SQL Server database
///
public class DomainMembershipProvider : MembershipProvider
{
const string EventSource = "DomainMembershipProvider";
const string ExceptionMessage = "An exception occurred. Please check the log.";
#region Overridden Properties
#region ApplicationName
///
/// The name of the application using the custom membership provider.
///
///
///
/// The name of the application using the custom membership provider.
///
public override string ApplicationName
{
get;
set;
}
#endregion
#region EnablePasswordReset
bool _enablePasswordReset;
///
/// 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;
}
}
#endregion
#region EnablePasswordRetrieval
bool _enablePasswordRetrieval;
///
/// 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 true.
///
public override bool EnablePasswordRetrieval
{
get
{
return _enablePasswordRetrieval;
}
}
#endregion
#region MaxInvalidPasswordAttempts
int _maxInvalidPasswordAttempts;
///
/// 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;
}
}
#endregion
#region MinRequiredNonAlphanumericCharacters
int _minRequiredNonAlphanumericCharacters;
///
/// 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;
}
}
#endregion
#region MinRequiredPasswordLength
int _minRequiredPasswordLength;
///
/// Gets the minimum length required for a password.
///
///
///
/// The minimum length required for a password.
///
public override int MinRequiredPasswordLength
{
get
{
return _minRequiredPasswordLength;
}
}
#endregion
#region PasswordAttemptWindow
int _passwordAttemptWindow;
///
/// 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;
}
}
#endregion
#region PasswordFormat
MembershipPasswordFormat _passwordFormat;
///
/// 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;
}
}
#endregion
#region PasswordStrengthRegularExpression
string _passwordStrengthRegularExpression;
///
/// Gets the regular expression used to evaluate a password.
///
///
///
/// A regular expression used to evaluate a password.
///
public override string PasswordStrengthRegularExpression
{
get
{
return _passwordStrengthRegularExpression;
}
}
#endregion
#region RequiresQuestionAndAnswer
bool _requiresQuestionAndAnswer;
///
/// 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;
}
}
#endregion
#region RequiresUniqueEmail
bool _requiresUniqueEmail;
///
/// 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;
}
}
#endregion
#region WriteExceptionsToEventLog
///
/// Gets or sets a value indicating whether to write exceptions to the event log.
///
public bool WriteExceptionsToEventLog
{
get;
set;
}
#endregion
#endregion
string _connectionString;
string _rsaKey;
#region Initialize
///
/// 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.
///
///
/// The name of the provider has a length of zero.
///
///
/// An attempt is made to call on a provider after the provider has already been initialized.
///
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
#region Initialize values from web.config
if(config == null)
{
throw new ArgumentNullException("config");
}
if(String.IsNullOrEmpty(name))
{
name = "DomainMembershipProvider";
}
if(String.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Domain Membership provider");
}
#endregion
// Initialize the abstract base class.
base.Initialize(name, config);
ApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
_maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
_passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
_minRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
_minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
_passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
_enablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
_enablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
_requiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "true"));
_requiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
WriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));
#region Get password format
string tempFormat = config["passwordFormat"] ?? "Hashed";
switch(tempFormat)
{
case "Hashed":
_passwordFormat = MembershipPasswordFormat.Hashed;
break;
case "Encrypted":
_passwordFormat = MembershipPasswordFormat.Encrypted;
break;
case "Clear":
_passwordFormat = MembershipPasswordFormat.Clear;
break;
default:
throw new ProviderException("Password format not supported.");
}
#endregion
#region Initialize connection string
ConnectionStringSettings connectionStringSettings =
ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
if(connectionStringSettings == null || connectionStringSettings.ConnectionString.Trim() == "")
{
throw new ProviderException("Connection string cannot be blank.");
}
_connectionString = connectionStringSettings.ConnectionString;
#endregion
var rsaSettings = RsaSettings.GetSection();
if(rsaSettings != null)
{
_rsaKey = rsaSettings.Key;
}
#region Get encryption and decryption key information from the configuration
var cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
var machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");
if(machineKey.ValidationKey.Contains("AutoGenerate"))
{
if(PasswordFormat != MembershipPasswordFormat.Clear)
{
throw new ProviderException("Hashed or Encrypted passwords are not supported with auto-generated keys.");
}
}
#endregion
}
#endregion
#region GetConfigValue
static string GetConfigValue(string configValue, string defaultValue)
{
return String.IsNullOrEmpty(configValue) ? defaultValue : configValue;
}
#endregion
/////////////////////////////////////////////////////////////////////
// Overridden methods
/////////////////////////////////////////////////////////////////////
#region ChangePassword
///
/// Changes the password.
///
/// Name of the user.
/// The old password - obfuscated.
/// The new password - encrypted.
/// true if successful; false otherwise.
public override bool ChangePassword(string userName, string oldPassword, string newPassword)
{
if(!ValidateUser(userName, oldPassword))
{
return false;
}
var thePassword = (_rsaKey != null)
? RsaProvider.Decrypt(_rsaKey, newPassword)
: newPassword;
var args = new ValidatePasswordEventArgs(userName, thePassword, false);
OnValidatingPassword(args);
if(args.Cancel)
{
if(args.FailureInformation != null)
{
throw args.FailureInformation;
}
throw new MembershipPasswordException("Change password canceled due to new password validation failure.");
}
bool success;
try
{
DbSavePassword(userName, thePassword, false);
success = true;
}
catch(Exception e)
{
success = false;
if(WriteExceptionsToEventLog)
{
LogUtility.WriteApplicationEventLog(e);
throw new ProviderException(ExceptionMessage);
}
}
return success;
}
#endregion
#region ChangePasswordQuestionAndAnswer
///
/// 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(!ValidateUser(userName, password))
{
return false;
}
var theAnswer = (_rsaKey != null)
? RsaProvider.Decrypt(_rsaKey, newPasswordAnswer)
: newPasswordAnswer;
bool success;
try
{
DbSavePasswordQuestionAndAnswer(userName, newPasswordQuestion, theAnswer);
success = true;
}
catch(Exception e)
{
success = false;
if(WriteExceptionsToEventLog)
{
LogUtility.WriteApplicationEventLog(e);
throw new ProviderException(ExceptionMessage);
}
}
return success;
}
#endregion
#region ResetPassword
///
/// 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.
/// The new password for the specified user.
public override string ResetPassword(string userName, string answer)
{
bool isValid;
try
{
isValid = ValidateAnswer(userName, answer);
}
catch(Exception e)
{
if(WriteExceptionsToEventLog)
{
LogUtility.WriteApplicationEventLog(e);
throw new ProviderException(ExceptionMessage);
}
throw;
}
if(!isValid)
{
throw new ProviderException("Invalid user and/or answer.");
}
return ResetPassword(userName);
}
///
/// Resets a user's password to a new, automatically generated password.
///
/// The user to reset the password for.
/// The new password for the specified user.
public string ResetPassword(string userName)
{
var newPassword = Membership.GeneratePassword(11, 0);
DbSavePassword(userName, newPassword, true);
return newPassword;
}
#endregion
#region ValidateUser
///
/// Validates the user.
///
/// The name of the user to be validated.
/// The password to be validated.
/// true if authentication succeeded; false otherwise.
public override bool ValidateUser(string userName, string password)
{
bool isValid;
try
{
isValid = ValidateCredentials(userName, password);
}
catch(Exception e)
{
if(WriteExceptionsToEventLog)
{
LogUtility.WriteApplicationEventLog(e);
throw new ProviderException(ExceptionMessage);
}
throw;
}
return isValid;
}
#endregion
#region Not Implemented
///
/// 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.
/// This method is not implemented.
///
/// 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)
{
throw new NotImplementedException();
}
///
/// 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.
/// This method is not implemented.
///
/// true if the user was successfully deleted; otherwise, false.
///
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
}
///
/// 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. is zero-based.
/// The size of the page of results to return.
/// The total number of matched users.
/// This method is not implemented.
///
/// A collection that contains a page of objects beginning at the page specified by .
///
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new 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. is zero-based.
/// The size of the page of results to return.
/// The total number of matched users.
/// This method is not implemented.
///
/// A collection that contains a page of objects beginning at the page specified by .
///
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
///
/// Gets a collection of all the users in the data source in pages of data.
///
/// The index of the page of results to return. is zero-based.
/// The size of the page of results to return.
/// The total number of matched users.
/// This method is not implemented.
///
/// A collection that contains a page of objects beginning at the page specified by .
///
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
///
/// Gets the number of users currently accessing the application.
///
/// This method is not implemented.
///
/// The number of users currently accessing the application.
///
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
///
/// 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.
/// This method is not implemented.
///
/// The password for the specified user name.
///
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
///
/// 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.
/// This method is not implemented.
///
/// A object populated with the specified user's information from the data source.
///
public override MembershipUser GetUser(string username, bool userIsOnline)
{
throw new NotImplementedException();
}
///
/// Gets user information from the data source 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.
/// This method is not implemented.
///
/// A object populated with the specified user's information from the data source.
///
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
}
///
/// Gets the user name associated with the specified e-mail address.
///
/// The e-mail address to search for.
/// This method is not implemented.
///
/// The user name associated with the specified e-mail address. If no match is found, return null.
///
public override string GetUserNameByEmail(string email)
{
throw new NotImplementedException();
}
///
/// Clears a lock so that the membership user can be validated.
///
/// The membership user whose lock status you want to clear.
/// This method is not implemented.
///
/// true if the membership user was successfully unlocked; otherwise, false.
///
public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
}
///
/// Updates information about a user in the data source.
///
/// A object that represents the user to update and the updated information for the user.
/// This method is not implemented.
public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
}
#endregion
/////////////////////////////////////////////////////////////////////
// Custom methods
/////////////////////////////////////////////////////////////////////
#region GetToken
///
/// Initializes the login/password change process by retrieving the user's password salt and a random challenge salt.
///
/// The user name to be queried.
/// The user's authentication token if the user was found; null otherwise.
public string GetToken(string userName)
{
return GetChallengeXmlToken("GetToken", userName, DbGetPasswordSalt, DbSavePasswordChallenge);
}
#endregion
#region GetPasswordQuestion
///
/// Initializes the password reset process by retrieving the user's password answer salt and a random challenge salt.
///
/// The user name to be queried.
/// The user's password answer token if the user was found; null otherwise.
public string GetPasswordQuestion(string userName)
{
return DbGetPasswordQuestion(userName);
}
#endregion
#region GetResetToken
///
/// Initializes the password reset process by retrieving the user's password answer salt and a random challenge salt.
///
/// The user name to be queried.
/// The user's authentication token if the user was found; null otherwise.
public string GetResetToken(string userName)
{
return GetChallengeXmlToken("GetPasswordQuestion", userName, DbGetPasswordAnswerSalt, DbSavePasswordAnswerChallenge, DbGetPasswordQuestion);
}
#endregion
#region GetPasswordQuestions
///
/// Gets a list of security questions.
///
/// A list of security questions.
public IEnumerable GetPasswordQuestions()
{
return DbGetPasswordQuestions();
}
#endregion
#region GetUserFingerprintTemplates
///
/// Gets a list of security questions.
///
/// A datatable containing userid, name and fingerprinttemplate.
public DataTable GetUserFingerprintTemplates()
{
return DbGetUserFingerprintTemplates();
}
#endregion
/////////////////////////////////////////////////////////////////////
// Database access methods
/////////////////////////////////////////////////////////////////////
#region GetUserID
///
/// Gets the user ID.
///
/// The name of the user to retrieved.
/// The authentication mode.
/// The ID of the user; -1 if not found.
public long GetUserID(string userName, AuthenticationMode authenticationMode)
{
long? result;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserID";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", authenticationMode.ToString());
cmd.Connection.Open();
result = cmd.ExecuteScalar().ToInt64();
}
}
return result.HasValue ? result.Value : -1;
}
#endregion
#region GetUserName
///
/// Gets the user name.
///
/// The NT account of the user to retrieved.
/// The name of the user; null if not found.
public string GetUserName(string ntAccount)
{
string result;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserName";
cmd.Parameters.AddWithValue("@NTAccount", ntAccount);
cmd.Connection.Open();
result = cmd.ExecuteScalar().ToStringSafe();
}
}
return string.IsNullOrEmpty(result) ? null : result;
}
#endregion
#region DbGetPasswordSalt
string DbGetPasswordSalt(string userName)
{
string result;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserPasswordSalt";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Connection.Open();
result = cmd.ExecuteScalar().ToStringSafe();
}
}
return result;
}
#endregion
#region DbGetPasswordHash
void DbGetPasswordHash(string userName, out string passwordHash, out string passwordSalt, out string challenge)
{
passwordHash = string.Empty;
passwordSalt = string.Empty;
challenge = string.Empty;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserPasswordHash";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Connection.Open();
using(var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if(reader.HasRows && reader.Read())
{
passwordHash = reader["PasswordHash"].ToStringSafe();
passwordSalt = reader["PasswordSalt"].ToStringSafe();
challenge = reader["PasswordChallenge"].ToStringSafe();
}
}
}
}
}
#endregion
#region DbSavePasswordChallenge
void DbSavePasswordChallenge(string userName, string challenge)
{
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_SaveUserPasswordChallenge";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Parameters.AddWithValue("@PasswordChallenge", challenge);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
}
#endregion
#region DbGetPasswordQuestion
string DbGetPasswordQuestion(string userName)
{
string result;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserPasswordQuestion";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Connection.Open();
result = cmd.ExecuteScalar().ToStringSafe();
}
}
return result;
}
#endregion
#region DbGetPasswordQuestions
IEnumerable DbGetPasswordQuestions()
{
var questions = new List();
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetPasswordQuestions";
cmd.Connection.Open();
using(var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while(reader.Read())
{
questions.Add(reader[0].ToStringSafe());
}
}
}
}
return questions;
}
#endregion
#region DbGetPasswordAnswerSalt
string DbGetPasswordAnswerSalt(string userName)
{
string result;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserPasswordAnswerSalt";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Connection.Open();
result = cmd.ExecuteScalar().ToStringSafe();
}
}
return result;
}
#endregion
#region DbGetPasswordAnswerHash
void DbGetPasswordAnswerHash(string userName, out string passwordAnswerHash, out string passwordAnswerSalt, out string challenge)
{
passwordAnswerHash = string.Empty;
passwordAnswerSalt = string.Empty;
challenge = string.Empty;
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserPasswordAnswerHash";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Connection.Open();
using(var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if(reader.HasRows && reader.Read())
{
passwordAnswerHash = reader["PasswordAnswerHash"].ToStringSafe();
passwordAnswerSalt = reader["PasswordAnswerSalt"].ToStringSafe();
challenge = reader["PasswordAnswerChallenge"].ToStringSafe();
}
}
}
}
}
#endregion
#region DbSavePasswordAnswerChallenge
void DbSavePasswordAnswerChallenge(string userName, string challenge)
{
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_SaveUserPasswordAnswerChallenge";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Parameters.AddWithValue("@PasswordAnswerChallenge", challenge);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
}
#endregion
#region DbSavePassword
void DbSavePassword(string userName, string newPassword, bool isPasswordReset)
{
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_SaveUserPassword";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Parameters.AddWithValue("@NewPassword", newPassword);
cmd.Parameters.AddWithValue("@IsPasswordReset", isPasswordReset);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
}
#endregion
#region DbSavePasswordQuestionAndAnswer
void DbSavePasswordQuestionAndAnswer(string userName, string question, string answer)
{
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_SaveUserPasswordQuestionAnswer";
cmd.Parameters.AddWithValue("@UserName", userName);
cmd.Parameters.AddWithValue("@AuthMode", DomainAuthenticationProvider.AuthenticationMode.ToString());
cmd.Parameters.AddWithValue("@PasswordQuestion", question);
cmd.Parameters.AddWithValue("@PasswordAnswer", answer);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
}
#endregion
#region DbGetUserFingerprintTemplates
DataTable DbGetUserFingerprintTemplates()
{
var fingerprints = new DataTable();
using(var conn = new SqlConnection(_connectionString))
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Security.Pr_GetUserFingerprintTemplates";
cmd.Connection.Open();
using(var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
fingerprints.Load(reader);
}
}
}
return fingerprints;
}
#endregion
///////////////////////////////////////////////////////////////////
// Helper methods
///////////////////////////////////////////////////////////////////
#region ValidateCredentials
///
/// Validates the user's credentials against the database.
///
/// The user name to be validated.
/// The credential to be validated.
/// true if the credentials are valid; false otherwise.
bool ValidateCredentials(string userName, string credential)
{
string challenge;
string password;
string salt;
DbGetPasswordHash(userName, out password, out salt, out challenge);
var dbCredential = SecurePassword.Obfuscate(password, salt, challenge);
return credential == dbCredential;
}
#endregion
#region ValidateAnswer
///
/// Validates the user's credentials against the database.
///
/// The user name to be validated.
/// The answer to be validated.
/// true if the answer is valid; false otherwise.
bool ValidateAnswer(string userName, string answer)
{
string challenge;
string answerHash;
string salt;
DbGetPasswordAnswerHash(userName, out answerHash, out salt, out challenge);
var dbAnswer = SecurePassword.Obfuscate(answerHash, salt, challenge);
return answer == dbAnswer;
}
#endregion
#region GetChallengeXmlToken
string GetChallengeXmlToken(string tokenType, string userName, Func getSalt, Action saveChallenge, Func getContent = null)
{
if(!String.IsNullOrEmpty(userName))
{
try
{
// returns null if userName does not exist
string content = null;
if(getContent != null)
{
content = getContent(userName);
}
var salt = getSalt(userName);
if(!String.IsNullOrEmpty(salt))
{
var challenge = SecurePassword.CreateSalt();
saveChallenge(userName, challenge);
var token = new XElement("Token",
new XAttribute("Salt", salt),
new XAttribute("Challenge", challenge)
);
if(content != null)
{
token.Value = content;
}
return token.ToString();
}
}
catch(Exception e)
{
if(WriteExceptionsToEventLog)
{
LogUtility.WriteApplicationEventLog(e);
throw new ProviderException(ExceptionMessage);
}
throw;
}
}
return null;
}
#endregion
}
}