Files
ntpa/MySql/Source/MySql.Web/SimpleMembershipProvider.cs
T
2016-03-16 20:08:36 +01:00

1107 lines
40 KiB
C#

// Copyright © 2014, 2015 Oracle and/or its affiliates. All rights reserved.
//
// MySQL Connector/NET is licensed under the terms of the GPLv2
// <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
// MySQL Connectors. There are special exceptions to the terms and
// conditions of the GPLv2 as it is applied to this software, see the
// FLOSS License Exception
// <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
using MySql.Web.General;
using MySql.Web.Properties;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Configuration.Provider;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Hosting;
using System.Web.Security;
using WebMatrix.WebData;
using System.Security.Cryptography;
using System.Web;
using System.Resources;
using MySql.Web.Common;
namespace MySql.Web.Security
{
public class MySqlSimpleMembershipProvider : ExtendedMembershipProvider
{
#region Private
private readonly MembershipProvider _prevProvider;
Application _app;
bool _enablePwdReset;
bool _enablePwdRetrival;
int _maxPwdAttempts;
int _minReqNonAlphanumericalChars;
int _minReqPwdLength;
int _pwdAttemptWindow;
MembershipPasswordFormat _pwdFormat;
string _pwdStrenghtRegex;
bool _reqQuestionAnswer;
bool _reqUniqueEmail;
string _connString;
string _userTableName;
string _userIdColumn;
string _userNameColumn;
bool _autoGenerateTables;
private readonly string _membershipTable = "webpages_membership";
private readonly string _oauthMembershipTable = "webpages_oauthmembership";
private readonly string _userInRolesTable = "webpages_usersinroles";
private readonly string _oauthTokenTable = "webpages_oauthtoken";
private static string GetConfigValue(string configVal, string defaultVal)
{
return !string.IsNullOrEmpty(configVal) ? configVal : defaultVal;
}
#endregion
public MySqlSimpleMembershipProvider()
: this(null)
{ }
public MySqlSimpleMembershipProvider(MembershipProvider previousProvider)
{
_prevProvider = previousProvider;
if (_prevProvider != null)
{
_prevProvider.ValidatingPassword += delegate(object sender, ValidatePasswordEventArgs args) { this.OnValidatingPassword(args); };
}
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
if (config == null)
{
NullArgumentException("config");
}
if (string.IsNullOrEmpty(name))
{
name = "MySqlExtendedMembershipProvider";
}
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", string.Format("MySql Default {0} Description", name));
}
base.Initialize(name, config);
var appName = GetConfigValue(config["applicationName"], HostingEnvironment.SiteName);
_maxPwdAttempts = Int32.Parse(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
_pwdAttemptWindow = Int32.Parse(GetConfigValue(config["passwordAttemptWindow"], "10"));
_minReqNonAlphanumericalChars = Int32.Parse(GetConfigValue(config["minRequiredNonalphanumericCharacters"], "1"));
_minReqPwdLength = Int32.Parse(GetConfigValue(config["minRequiredPasswordLength"], "7"));
_pwdStrenghtRegex = GetConfigValue(config["passwordStrengthRegularExpression"], "");
_enablePwdReset = bool.Parse(GetConfigValue(config["enablePasswordReset"], "True"));
_enablePwdRetrival = bool.Parse(GetConfigValue(config["enablePasswordRetrieval"], "False"));
_reqQuestionAnswer = bool.Parse(GetConfigValue(config["requiresQuestionAndAnswer"], "False"));
_reqUniqueEmail = bool.Parse(GetConfigValue(config["requiresUniqueEmail"], "True"));
var pwdFormat = !string.IsNullOrEmpty(config["passwordFormat"]) ? config["passwordFormat"].ToString().ToLowerInvariant() : "hashed";
switch (pwdFormat)
{
case "hashed":
_pwdFormat = MembershipPasswordFormat.Hashed;
break;
case "encrypted":
_pwdFormat = MembershipPasswordFormat.Encrypted;
break;
case "clear":
_pwdFormat = MembershipPasswordFormat.Clear;
break;
default:
throw new ProviderException(Resources.PasswordFormatNotSupported);
}
if (_pwdFormat == MembershipPasswordFormat.Hashed)
{
if (_enablePwdRetrival)
throw new ProviderException(Resources.CannotRetrieveHashedPasswords);
}
_app = new Application(appName, base.Description);
ConnectionStringSettings connStrSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
_connString = connStrSettings != null ? connStrSettings.ConnectionString.Trim() : "";
if (string.IsNullOrEmpty(_connString)) return;
UserTableName = GetConfigValue(config["userTableName"], "");
UserIdColumn = GetConfigValue(config["userIdColumn"], "");
UserNameColumn = GetConfigValue(config["userNameColumn"], "");
_autoGenerateTables = bool.Parse(GetConfigValue(config["autoGenerateTables"], "True"));
if (_autoGenerateTables)
CreateTables();
else
ValidateUserTable();
Initialized = true;
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
if (!Initialized)
{
_prevProvider.ChangePassword(username, oldPassword, newPassword);
}
if (string.IsNullOrEmpty(username))
NullArgumentException("username");
if (string.IsNullOrEmpty(oldPassword))
NullArgumentException("oldPassword");
if (string.IsNullOrEmpty(newPassword))
NullArgumentException("newPassword");
int userid = GetUserId(username);
if (userid <= 0)
{
return false;
}
if (VerifyPassword(userid, oldPassword, GetHashedUserPassword(userid)))
{
return UpdatePassword(userid, newPassword) > 0;
}
return false;
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
IsValidOperation(false);
return _prevProvider.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer);
}
public override bool ConfirmAccount(string accountConfirmationToken)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var token = dbConn.ExecuteQuerySingleRecord(string.Format("select userid, confirmationtoken from {0} where confirmationtoken=?", _membershipTable), accountConfirmationToken);
if (token == null || (token != null && string.IsNullOrEmpty(token[1].ToString())))
{
return false;
}
return dbConn.ExecuteNonQuery(string.Format("update {0} set isconfirmed=1 where userid=?;", _membershipTable), (int)token[0]) > 0;
}
}
public override bool ConfirmAccount(string userName, string accountConfirmationToken)
{
var userid = GetUserId(userName);
if (userid <= 0)
return false;
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var token = dbConn.ExecuteQuerySingleRecord(string.Format("select userid, confirmationtoken from {0} where confirmationtoken=? and userid=?", _membershipTable), accountConfirmationToken, userid);
if (token == null || (token != null && string.IsNullOrEmpty(token[1].ToString())))
{
return false;
}
return dbConn.ExecuteNonQuery(string.Format("update {0} set isconfirmed=1 where userid=?;", _membershipTable), userid) > 0;
}
}
public override string CreateAccount(string userName, string password, bool requireConfirmationToken)
{
IsValidOperation(true);
if (string.IsNullOrEmpty(userName))
NullArgumentException(userName);
if (string.IsNullOrEmpty(password))
NullArgumentException(password);
var hashedPass = HashPassword(password);
if (hashedPass.Length > 128)
throw new ArgumentException(Resources.PasswordExceedsMaxLength, password);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var userid = GetUserId(userName);
if (userid <= 0)
{
throw new InvalidOperationException(string.Format(Resources.UserNotFound, UserTableName));
}
if (dbConn.ExecuteQuerySingleRecord(string.Format("select userid from {0} where userid=?;", _membershipTable), userid) != null)
{
throw new InvalidOperationException(string.Format(Resources.UserAlreadyExists, userName));
}
object token = DBNull.Value;
if (requireConfirmationToken)
token = GenerateToken();
string insertQuery = string.Format("insert into {0} (userid, createdate, confirmationtoken, isconfirmed, password, passwordchangeddate, passwordsalt) values(?,now(),?,?,?,now(),?)", _membershipTable);
if (dbConn.ExecuteNonQuery(insertQuery, userid, token, !requireConfirmationToken, hashedPass, string.Empty) <= 0)
{
throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError);
}
return token == DBNull.Value ? null : token.ToString();
}
}
public override void CreateOrUpdateOAuthAccount(string provider, string providerUserId, string userName)
{
IsValidOperation(true);
if (string.IsNullOrEmpty(userName))
NullArgumentException(userName);
var userid = GetUserId(userName);
if (userid <= 0)
{
throw new InvalidOperationException(string.Format(Resources.UserNotFound, UserTableName));
}
var oauthUserId = GetUserIdFromOAuth(provider, providerUserId);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (oauthUserId == 0)
{
if (dbConn.ExecuteNonQuery(string.Format("insert into {0} (provider, provideruserid, userid) values(?,?,?)", _oauthMembershipTable), provider, providerUserId, userid) <= 0)
{
throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError);
}
}
else
{
if (dbConn.ExecuteNonQuery(string.Format("update {0} set userid=? where upper(provider) = ? and upper(provideruserid)=?;", _oauthMembershipTable), userid, provider.ToUpper(), providerUserId.ToUpper()) <= 0)
{
throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError);
}
}
}
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
IsValidOperation(false);
return _prevProvider.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
}
public override string CreateUserAndAccount(string userName, string password, bool requireConfirmation, IDictionary<string, object> values)
{
IsValidOperation(true);
CreateUserInUserTable(userName, values);
return CreateAccount(userName, password, requireConfirmation);
}
public override bool DeleteAccount(string userName)
{
IsValidOperation(true);
int userid = GetUserId(userName);
if (userid < 0)
{
return false;
}
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return (dbConn.ExecuteNonQuery(string.Format("delete from {0} where userid=?;", _membershipTable), userid) > 0);
}
}
public override void DeleteOAuthAccount(string provider, string providerUserId)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (dbConn.ExecuteNonQuery(string.Format("delete from {0} where upper(provider) = ? and upper(provideruserid)=?;", _oauthMembershipTable), provider.ToUpper(), providerUserId.ToUpper()) <= 0)
{
throw new Exception(string.Format(Resources.DeleteOAuthAccountFailed, provider, providerUserId));
}
}
}
public override void DeleteOAuthToken(string token)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
dbConn.ExecuteNonQuery(string.Format("delete from {0} where token=?", _oauthTokenTable), token);
}
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
if (!Initialized)
return _prevProvider.DeleteUser(username, deleteAllRelatedData);
int userid = GetUserId(username);
if (userid < 0)
return false;
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (deleteAllRelatedData)
{
return dbConn.ExecuteInTransaction(
new List<Tuple<string, object[]>>()
{
new Tuple<string, object[]>(string.Format("delete from {0} where {1}=?;", UserTableName, UserIdColumn), new object [] { userid }),
new Tuple<string, object[]>(string.Format("delete from {0} where userid=?;", _oauthMembershipTable), new object [] { userid }),
new Tuple<string, object[]>(string.Format("delete from {0} where userid=?;", _membershipTable), new object [] { userid }),
new Tuple<string, object[]>(string.Format("delete from {0} where userid=?;", _userInRolesTable), new object [] { userid })
});
}
else
{
return (dbConn.ExecuteNonQuery(string.Format("delete from {0} where {1}=?;", UserTableName, UserIdColumn), userid) > 0);
}
}
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
IsValidOperation(false);
return _prevProvider.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords);
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
IsValidOperation(false);
return _prevProvider.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords);
}
public override string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow)
{
IsValidOperation(true);
if (string.IsNullOrEmpty(userName))
NullArgumentException("username");
int userid = GetUserId(userName);
if (userid <= 0)
InvalidUserException(userName);
if (UserConfirmed(userid))
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
string token = dbConn.ExecuteScalar(string.Format("select PasswordVerificationToken from {0} where userid=? and PasswordVerificationTokenExpirationDate > ?;", _membershipTable), userid, DateTime.Now) as string;
if (token != null)
{
token = GenerateToken();
if (dbConn.ExecuteNonQuery(string.Format("update {0} set PasswordVerificationToken=?, PasswordVerificationTokenExpirationDate=? where userid=?;", _membershipTable), token, DateTime.Now.AddMinutes(tokenExpirationInMinutesFromNow), userid) <= 0)
{
throw new ProviderException(Resources.GeneratePassVerificationTokenFailed);
}
}
return token;
}
}
return null;
}
public override ICollection<OAuthAccountData> GetAccountsForUser(string userName)
{
IsValidOperation(true);
int userid = GetUserId(userName);
if (userid > 0)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var records = dbConn.ExecuteQuery(string.Format("select provider, provideruserid from {0} where userid=?", _oauthMembershipTable), userid);
if (records != null && records.Count() > 0)
{
var accounts = new List<OAuthAccountData>();
records.ToList().ForEach(record => accounts.Add(new OAuthAccountData(record["provider"].ToString(), record["provideruserid"].ToString())));
return accounts;
}
}
}
return new OAuthAccountData[0];
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
IsValidOperation(false);
return _prevProvider.GetAllUsers(pageIndex, pageSize, out totalRecords);
}
public override DateTime GetCreateDate(string userName)
{
int userid = GetUserId(userName);
if (userid < 0)
InvalidUserException(userName);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var createDate = dbConn.ExecuteScalar(string.Format("select CreateDate from {0} where userid=?;", _membershipTable), userid);
if (createDate != null && createDate != DBNull.Value)
return (DateTime)createDate;
return DateTime.MinValue;
}
}
public override DateTime GetLastPasswordFailureDate(string userName)
{
int userid = GetUserId(userName);
if (userid < 0)
InvalidUserException(userName);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var failureDate = dbConn.ExecuteScalar(string.Format("select LastPasswordFailureDate from {0} where userid=?;", _membershipTable), userid);
if (failureDate != null && failureDate != DBNull.Value)
return (DateTime)failureDate;
return DateTime.MinValue;
}
}
public override int GetNumberOfUsersOnline()
{
IsValidOperation(false);
return _prevProvider.GetNumberOfUsersOnline();
}
public override string GetPassword(string username, string answer)
{
IsValidOperation(false);
return _prevProvider.GetPassword(username, answer);
}
public override DateTime GetPasswordChangedDate(string userName)
{
int userid = GetUserId(userName);
if (userid < 0)
InvalidUserException(userName);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var changedDate = dbConn.ExecuteScalar(string.Format("select PasswordChangedDate from {0} where userid=?;", _membershipTable), userid);
if (changedDate != null)
return (DateTime)changedDate;
return DateTime.MinValue;
}
}
public override int GetPasswordFailuresSinceLastSuccess(string userName)
{
int userid = GetUserId(userName);
if (userid < 0)
InvalidUserException(userName);
return GetPasswordFailuresSinceLastSuccess(userid);
}
private int GetPasswordFailuresSinceLastSuccess(int userId)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var failures = dbConn.ExecuteScalar(string.Format("select PasswordFailuresSinceLastSuccess from {0} where userid=?;", _membershipTable), userId);
if (failures != null)
return (int)failures;
return -1;
}
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
IsValidOperation(false);
return _prevProvider.GetUser(providerUserKey, userIsOnline);
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (!Initialized)
return _prevProvider.GetUser(username, userIsOnline);
int userid = GetUserId(username);
if (userid < 0)
{
return null;
}
return new MembershipUser(Membership.Provider.Name, username, userid, null, null, null, true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
}
public int GetUserId(string userName)
{
return GetUserId(userName, GetConnectionString(), UserTableName, UserIdColumn, UserNameColumn);
}
internal static int GetUserId(string userName, string connectionString, string userTableName, string userIdColumn, string userNameColumn)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(connectionString))
{
var user = dbConn.ExecuteQuerySingleRecord(string.Format("select {0} from {1} where {2} = ?;", userIdColumn, userTableName, userNameColumn), userName);
if (user != null)
return (int)user[userIdColumn];
return 0;
}
}
public override int GetUserIdFromOAuth(string provider, string providerUserId)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var user = dbConn.ExecuteQuerySingleRecord(string.Format("select userid from {0} where upper(provider) = ? and upper(provideruserid)=?;", _oauthMembershipTable), provider.ToUpper(), providerUserId.ToUpper());
if (user != null)
return (int)user["userid"];
return 0;
}
}
public override int GetUserIdFromPasswordResetToken(string token)
{
IsValidOperation(true);
return GetUserIdFromPasswordResetToken(token, false);
}
public override string GetOAuthTokenSecret(string token)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return dbConn.ExecuteScalar(string.Format("select secret from {0} where token=?;", _oauthTokenTable), token) as string;
}
}
private int GetUserIdFromPasswordResetToken(string token, bool checkExpirationDate = false)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var user = dbConn.ExecuteQuerySingleRecord(string.Format("select userid from {0} where PasswordVerificationToken = ? {1};", _membershipTable, (checkExpirationDate ? "and PasswordVerificationTokenExpirationDate > now()" : "")), token);
if (user != null)
return (int)user["userid"];
return 0;
}
}
public override string GetUserNameByEmail(string email)
{
IsValidOperation(false);
return _prevProvider.GetUserNameByEmail(email);
}
public override string GetUserNameFromId(int userId)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return dbConn.ExecuteScalar(string.Format("select {0} from {1} where {2}=?;", UserNameColumn, UserTableName, UserIdColumn), userId) as string;
}
}
public override bool HasLocalAccount(int userId)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return (dbConn.ExecuteQuery(string.Format("select userid from {0} where userid=?;", _membershipTable), userId).Count() > 0);
}
}
public override bool IsConfirmed(string userName)
{
IsValidOperation(true);
if (string.IsNullOrEmpty(userName))
NullArgumentException("username");
int userid = GetUserId(userName);
if (userid <= 0)
InvalidUserException(userName);
return UserConfirmed(userid);
}
public override void ReplaceOAuthRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret)
{
IsValidOperation(true);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
dbConn.ExecuteNonQuery(string.Format("delete from {0} where token=?", _oauthTokenTable), requestToken);
}
StoreOAuthRequestToken(accessToken, accessTokenSecret);
}
public override string ResetPassword(string username, string answer)
{
IsValidOperation(false);
return _prevProvider.ResetPassword(username, answer);
}
public override bool ResetPasswordWithToken(string token, string newPassword)
{
IsValidOperation(true);
if (string.IsNullOrEmpty(token))
{
NullArgumentException("token");
}
if (string.IsNullOrEmpty(newPassword))
{
NullArgumentException("newPasword");
}
int userid = GetUserIdFromPasswordResetToken(token, true);
if (userid <= 0)
{
return false;
}
bool passUpdated = UpdatePassword(userid, newPassword) > 0;
if (passUpdated)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (dbConn.ExecuteNonQuery(string.Format("update {0} set PasswordVerificationToken=null, PasswordVerificationTokenExpirationDate=null where userid=?;", _membershipTable), userid) <= 0)
{
throw new ProviderException(string.Format(Resources.ClearPassTokenFailed, userid, _membershipTable));
}
}
}
return passUpdated;
}
public override void StoreOAuthRequestToken(string requestToken, string requestTokenSecret)
{
IsValidOperation(true);
string secret = GetOAuthTokenSecret(requestToken);
if (secret != null)
{
if (secret.Equals(requestTokenSecret, StringComparison.OrdinalIgnoreCase))
return;
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (dbConn.ExecuteNonQuery(string.Format("update {0} set secret=? where token=?;", _oauthTokenTable), requestTokenSecret, requestToken) <= 0)
throw new ProviderException(string.Format(Resources.UpdateTokenFailed, requestTokenSecret));
}
}
else
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (dbConn.ExecuteNonQuery(string.Format("insert into {0} (token, secret) values (?,?);", _oauthTokenTable), requestTokenSecret, requestToken) <= 0)
throw new ProviderException(string.Format(Resources.SaveTokenFailed, requestTokenSecret));
}
}
}
public override bool UnlockUser(string userName)
{
IsValidOperation(false);
return _prevProvider.UnlockUser(userName);
}
public override void UpdateUser(MembershipUser user)
{
IsValidOperation(false);
_prevProvider.UpdateUser(user);
}
public override bool ValidateUser(string username, string password)
{
if (!Initialized)
return _prevProvider.ValidateUser(username, password);
if (string.IsNullOrEmpty(username))
NullArgumentException("userName");
if (string.IsNullOrEmpty(password))
NullArgumentException("password");
int userid = GetUserId(username);
if (userid > 0)
{
if (!UserConfirmed(userid))
return false;
else
return VerifyPassword(userid, password, GetHashedUserPassword(userid));
}
return false;
}
#region Properties
public override string ApplicationName
{
get
{
if (Initialized)
throw new NotSupportedException();
else
return _prevProvider.ApplicationName;
}
set
{
if (Initialized)
throw new NotSupportedException();
else
_prevProvider.ApplicationName = value;
}
}
public string ConnectionString
{ get; set; }
public string ConnectionStringName
{ get; set; }
public override bool EnablePasswordReset
{
get
{
return Initialized ? false : _prevProvider.EnablePasswordReset;
}
}
public override bool EnablePasswordRetrieval
{
get
{
return Initialized ? false : _prevProvider.EnablePasswordRetrieval;
}
}
public override int MaxInvalidPasswordAttempts
{
get
{
return Initialized ? Int32.MaxValue : _prevProvider.MaxInvalidPasswordAttempts;
}
}
public override int MinRequiredPasswordLength
{
get
{
return Initialized ? 7 : _prevProvider.MinRequiredPasswordLength;
}
}
public override int MinRequiredNonAlphanumericCharacters
{
get
{
return Initialized ? 1 : _prevProvider.MinRequiredNonAlphanumericCharacters;
}
}
public override int PasswordAttemptWindow
{
get
{
return Initialized ? Int32.MaxValue : _prevProvider.PasswordAttemptWindow;
}
}
public override MembershipPasswordFormat PasswordFormat
{
get
{
return Initialized ? MembershipPasswordFormat.Hashed : _prevProvider.PasswordFormat;
}
}
public override string PasswordStrengthRegularExpression
{
get
{
return Initialized ? string.Empty : _prevProvider.PasswordStrengthRegularExpression;
}
}
public string ProviderName
{ get; set; }
public override bool RequiresQuestionAndAnswer
{
get
{
return Initialized ? false : _prevProvider.RequiresQuestionAndAnswer;
}
}
public override bool RequiresUniqueEmail
{
get
{
return Initialized ? false : _prevProvider.RequiresUniqueEmail;
}
}
public string UserTableName
{
get
{
if (string.IsNullOrEmpty(_userTableName))
throw new InvalidOperationException(Resources.UserTableNameNotInitilized);
return _userTableName;
}
internal set
{
_userTableName =value;
}
}
public string UserIdColumn
{
get
{
if (string.IsNullOrEmpty(_userIdColumn))
throw new InvalidOperationException(Resources.UserIdColumnNotInitialized);
return _userIdColumn;
}
internal set
{
_userIdColumn = value;
}
}
public string UserNameColumn
{
get
{
if (string.IsNullOrEmpty(_userNameColumn))
throw new InvalidOperationException(Resources.UserNameColumnNotInitialized);
return _userNameColumn;
}
internal set
{
_userNameColumn = value;
}
}
#endregion
#region Private_Internal
internal void CreateTables()
{
string connString = GetConnectionString();
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(connString))
{
if (!VerifyIfTableExists(connString, UserTableName))
{
dbConn.ExecuteNonQuery(string.Format("create table {0} ({1} int not null primary key auto_increment, {2} varchar(250) not null unique);", UserTableName, UserIdColumn, UserNameColumn));
}
//create schema
ResourceManager r = new ResourceManager("MySql.Web.Properties.Resources", typeof(SchemaManager).Assembly);
string schema = r.GetString("schema11");
dbConn.ExecuteNonQuery(schema);
}
}
internal void ValidateUserTable()
{
if (!VerifyIfTableExists(GetConnectionString(), UserTableName))
{
throw new InvalidOperationException(string.Format(Resources.UserTableNotFound, UserTableName));
}
}
internal bool Initialized
{
get;
set;
}
private string GetConnectionString()
{
if (!string.IsNullOrEmpty(ConnectionString))
return ConnectionString;
else
{
ConnectionStringSettings connString = ConfigurationManager.ConnectionStrings[ConnectionStringName];
if (connString != null)
return connString.ConnectionString;
}
if (!string.IsNullOrEmpty(_connString))
return _connString;
throw new InvalidOperationException(Resources.NoConnString);
}
internal static bool VerifyIfTableExists(string connectionString, string tableName)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(connectionString))
{
var tables = dbConn.ExecuteQuery("show tables;");
return tables.Where(record => record[0].ToString().Equals(tableName, StringComparison.OrdinalIgnoreCase)).Count() > 0;
}
}
internal string GetHashedUserPassword(int userId)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
var userPassword = dbConn.ExecuteQuerySingleRecord(string.Format("select password from {0} where userid=?;", _membershipTable), userId);
if (userPassword != null)
return userPassword[0].ToString();
return null;
}
}
internal string HashPassword(string password)
{
if (string.IsNullOrEmpty(password))
{
throw new ArgumentException(Resources.InvalidArgument, password);
}
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, 16, 1000);
byte[] salt = rfc2898.Salt;
byte[] passBytes = rfc2898.GetBytes(32);
byte[] result = new byte[48];
Buffer.BlockCopy(salt, 0, result, 0, 16);
Buffer.BlockCopy(passBytes, 0, result, 16, 32);
return Convert.ToBase64String(result);
}
internal bool VerifyPassword(int userid, string password, string hashedPassword)
{
if (string.IsNullOrEmpty(password))
{
throw new ArgumentException(Resources.InvalidArgument, password);
}
if (string.IsNullOrEmpty(hashedPassword))
{
throw new ArgumentException(Resources.InvalidArgument, hashedPassword);
}
byte[] hashed = Convert.FromBase64String(hashedPassword);
if (hashed.Length != 48)
{
return false;
}
byte[] salt = new byte[16];
byte[] passBytes = new byte[32];
Buffer.BlockCopy(hashed, 0, salt, 0, 16);
Buffer.BlockCopy(hashed, 16, passBytes, 0, 32);
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, salt, 1000);
bool validation = CompareBuffer(rfc2898.GetBytes(32), passBytes);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (validation)
{
dbConn.ExecuteNonQuery(string.Format("update {0} set PasswordFailuresSinceLastSuccess=0 where userid=?;", _membershipTable), userid);
}
else
{
int failures = GetPasswordFailuresSinceLastSuccess(userid);
dbConn.ExecuteNonQuery(string.Format("update {0} set PasswordFailuresSinceLastSuccess=?, LastPasswordFailureDate=now() where userid=?;", _membershipTable), (failures == -1 ? 1 : failures + 1), userid);
}
}
return validation;
}
internal bool CompareBuffer(byte[] source, byte[] target)
{
if (source == null || target == null || (source.Length != target.Length))
return false;
for (int ctr = 0; ctr < target.Length; ctr++)
{
if (target[ctr] != source[ctr])
return false;
}
return true;
}
private int UpdatePassword(int userId, string newPassword)
{
string hashedPass = HashPassword(newPassword);
if (hashedPass.Length > 128)
throw new ArgumentException(Resources.PasswordExceedsMaxLength, newPassword);
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return dbConn.ExecuteNonQuery(string.Format("update {0} set password=?, PasswordChangedDate=now(), PasswordSalt='' where userid=?;", _membershipTable), hashedPass, userId);
}
}
internal void IsValidOperation(bool currentProvider)
{
switch (currentProvider)
{
case true:
if (!Initialized)
ProviderException();
break;
case false:
if (Initialized)
PreviousProviderException();
break;
}
}
private void ProviderException()
{
throw new Exception(Resources.SimpleMembershipNotInitialized);
}
private void PreviousProviderException()
{
throw new NotSupportedException(Resources.PreviousProviderException);
}
private void InvalidUserException(string userName)
{
throw new Exception(string.Format(Resources.InvalidUser, userName, UserTableName));
}
internal static void NullArgumentException(string parameterName)
{
throw new ArgumentException(Resources.InvalidArgument, parameterName);
}
private string GenerateToken()
{
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
byte[] data = new byte[16];
provider.GetBytes(data);
return HttpServerUtility.UrlTokenEncode(data);
}
private void CreateUserInUserTable(string userName, IDictionary<string, object> values)
{
IsValidOperation(true);
var userid = GetUserId(userName);
if (userid > 0)
{
throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName);
}
StringBuilder columns = new StringBuilder();
columns.Append(UserNameColumn);
StringBuilder args = new StringBuilder();
args.Append("?");
var argsValues = new List<object>();
if (values != null)
{
foreach (var value in values)
{
if (string.Equals(UserNameColumn, value.Key, StringComparison.OrdinalIgnoreCase))
continue;
columns.Append(string.Format(",{0}", value.Value));
args.Append(",?");
argsValues.Add(value.Value != null ? value.Value : DBNull.Value);
}
}
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
if (dbConn.ExecuteNonQuery(string.Format("insert into {0} ({1}) values({2})", UserTableName, columns.ToString(), args.ToString()), userName, argsValues.ToArray()) < 1)
{
throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError);
}
}
}
private bool UserConfirmed(int userId)
{
using (MySqlDatabaseWrapper dbConn = new MySqlDatabaseWrapper(GetConnectionString()))
{
return ((long)dbConn.ExecuteScalar(string.Format("select count(*) from {0} where userid=? and isconfirmed=1;", _membershipTable), userId)) > 0;
}
}
#endregion
}
}