//*****************************************************************************
// This code file is part of the Universal Provider Framework for SharePoint.
// This file was written by Adam Buenz [WSS MVP] of ARB Security Solutions, LLC
// http://www.sharepointsecurity.com
//
// This file and its parts is free for re-distribution, for use in both free
// and commercial applications, however this header must remain intact for legal
// use. The Universal Provider Framework 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.
//*****************************************************************************
//**************************************
// Current Version: 1.0.0.0 (Beta)
//**************************************
// namespace references
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Hosting;
using System.Web.Security;
namespace Universal.SharePointProvider.Framework
{
///
/// Expose and inherit out of the membership services
///
public abstract class SharePointMembershipProvider : MembershipProvider
{
///
/// Declare constructor for the SharePointMembershipProvider class
///
///
///
///
protected SharePointMembershipProvider(string defaultName, string defaultDescription, DbProviderFactory provider)
{
this.eventLog = "Application";
this.exceptionMessage = "An exception has occurred with the SharePoint membership provider. Please contact your SharePoint administrator.";
this.defaultName = defaultName;
this.defaultDescription = defaultDescription;
this.provider = provider;
}
///
/// Declare neccesary variables
///
private bool _EnablePasswordReset;
private bool _EnablePasswordRetrieval;
private int _MaxInvalidPasswordAttempts;
private int _MinRequiredNonalphanumericCharacters;
private int _MinRequiredPasswordLength;
private int _PasswordAttemptWindow;
private bool _RequiresQuestionAndAnswer;
private bool _RequiresUniqueEmail;
private string connectionString;
private string defaultDescription;
private string defaultName;
private string eventLog;
private string exceptionMessage;
private string pApplicationName;
private MembershipPasswordFormat passwordFormat;
private ConnectionStringSettings pConnectionStringSettings;
private readonly DbProviderFactory provider;
private bool pWriteExceptionsToEventLog;
private SharePointUsersProvider usersProvider;
///
/// Declare relevant properties
///
public override string ApplicationName
{
get
{
return this.pApplicationName;
}
set
{
this.pApplicationName = value;
}
}
public override bool EnablePasswordReset
{
get
{
return this._EnablePasswordReset;
}
}
public override bool EnablePasswordRetrieval
{
get
{
return this._EnablePasswordRetrieval;
}
}
public override int MaxInvalidPasswordAttempts
{
get
{
return this._MaxInvalidPasswordAttempts;
}
}
public override int MinRequiredNonAlphanumericCharacters
{
get
{
return this._MinRequiredNonalphanumericCharacters;
}
}
public override int MinRequiredPasswordLength
{
get
{
return this._MinRequiredPasswordLength;
}
}
public override int PasswordAttemptWindow
{
get
{
return this._PasswordAttemptWindow;
}
}
public override MembershipPasswordFormat PasswordFormat
{
get
{
return this.passwordFormat;
}
}
public override string PasswordStrengthRegularExpression
{
get
{
return "";
}
}
public override bool RequiresQuestionAndAnswer
{
get
{
return this._RequiresQuestionAndAnswer;
}
}
public override bool RequiresUniqueEmail
{
get
{
return this._RequiresUniqueEmail;
}
}
public bool WriteExceptionsToEventLog
{
get
{
return this.pWriteExceptionsToEventLog;
}
set
{
this.pWriteExceptionsToEventLog = value;
}
}
protected virtual void ApplyParameterInfo(DbParameter parameter, DbType dbType, object value)
{
// Set the DBtype parameter
parameter.DbType = dbType;
// Set the value of the parameter
parameter.Value = value;
}
///
/// Update the users password in the custom pluggable data store
///
///
///
///
///
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
string message = null;
if ((string.IsNullOrEmpty(username) || string.IsNullOrEmpty(oldPassword)) || (string.IsNullOrEmpty(newPassword) || !this.IsStrongPassword(newPassword, out message)))
{
return false;
}
bool flagValid = this.CheckUsersPassword(username, oldPassword);
if (flagValid)
{
this.UpdateMembershipParameter(username, new object[] { "lastpasswordchangeddate", DbType.DateTime, DateTime.Now, "password", DbType.String, this.EncodePassword(newPassword) });
}
return flagValid;
}
///
/// Update the PasswordQuestionandAnswer in the custom data store
///
///
///
///
///
///
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
if ((string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) || (string.IsNullOrEmpty(newPasswordQuestion) || string.IsNullOrEmpty(newPasswordAnswer)))
{
return false;
}
bool flagValid = this.CheckUsersPassword(username, password);
if (flagValid)
{
this.UpdateMembershipParameter(username, new object[] { "passwordquestion", DbType.String, newPasswordQuestion, "passwordanswer", DbType.String, this.EncodePassword(newPasswordAnswer) });
}
return flagValid;
}
///
/// Check the user answer from the custom membership data store
///
///
///
///
///
private string CheckUsersAnswer(string username, string answer, bool retrivePassword)
{
if (string.IsNullOrEmpty(username))
{
throw new ArgumentNullException("username");
}
UserData userInformation = this.GetUserMembershipByUserName(username);
// Check whether the username is available within the custom datastore
if (userInformation == null)
{
throw new MembershipPasswordException("The provided SharePoint user name could not found.");
}
// Check whether the user is currently locked out
if (userInformation.membershipUser.IsLockedOut)
{
throw new MembershipPasswordException("The provided SharePoint user is currently locked out.");
}
string confirmationAnswer = userInformation.answer;
if (this.PasswordFormat == MembershipPasswordFormat.Encrypted)
{
confirmationAnswer = this.DecodePassword(userInformation.answer);
}
else if (this.PasswordFormat == MembershipPasswordFormat.Hashed)
{
answer = this.EncodePassword(answer);
}
if (this.RequiresQuestionAndAnswer && (confirmationAnswer.ToLower() != answer.ToLower()))
{
if (!this.GetFailedAttempts(username, false))
{
this.UpdateMembershipParameter(username, new object[] { "islockedout", DbType.Boolean, true, "lastlockoutdate", DbType.DateTime, DateTime.Now });
}
throw new MembershipPasswordException("Incorrect password answer provided.");
}
if (retrivePassword)
{
return this.DecodePassword(userInformation.password);
}
return null;
}
///
/// Check the users password from the custom membership data store
///
///
///
///
private bool CheckUsersPassword(string username, string password)
{
// determine whether the username or password is empty
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
return false;
}
UserData userInformation = this.GetUserMembershipByUserName(username);
bool flagValid = ((userInformation != null) && userInformation.membershipUser.IsApproved) && !userInformation.membershipUser.IsLockedOut;
if (flagValid)
{
string passwordString = userInformation.password;
if (this.PasswordFormat == MembershipPasswordFormat.Encrypted)
{
passwordString = this.DecodePassword(userInformation.password);
}
else if (this.PasswordFormat == MembershipPasswordFormat.Hashed)
{
password = this.EncodePassword(password);
}
flagValid = passwordString == password;
if (!flagValid && !this.GetFailedAttempts(username, true))
{
this.UpdateMembershipParameter(username, new object[] { "islockedout", DbType.Boolean, true, "lastlockoutdate", DbType.DateTime, DateTime.Now });
}
}
return flagValid;
}
///
/// Specify command creation
///
///
///
///
private DbCommand CreateCommand(string commandText, DbConnection connection)
{
DbCommand membershipDbCommand = this.provider.CreateCommand();
membershipDbCommand.Connection = connection;
membershipDbCommand.CommandText = commandText;
return membershipDbCommand;
}
///
/// Specify Parameter Creation
///
///
///
///
///
private DbParameter CreateParameter(string name, DbType dbType, object value)
{
DbParameter membershipDbParameter = this.provider.CreateParameter();
membershipDbParameter.ParameterName = name;
this.ApplyParameterInfo(membershipDbParameter, dbType, value);
return membershipDbParameter;
}
///
/// Specify Parameter Creation
///
///
///
///
///
///
private DbParameter CreateParameter(string name, DbType dbType, int size, object value)
{
DbParameter membershipDbParameter = this.provider.CreateParameter();
membershipDbParameter.ParameterName = name;
membershipDbParameter.Size = size;
this.ApplyParameterInfo(membershipDbParameter, dbType, value);
return membershipDbParameter;
}
///
/// Create a New User In The Custom Membership Data Store
///
///
///
///
///
///
///
///
///
///
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object userId, out MembershipCreateStatus status)
{
DbConnection createUserDbConnection = null;
IDbCommand createUserDbCommand = null;
MembershipCreateStatus userCreationStatus = MembershipCreateStatus.Success;
MembershipUser sharepointUser = null;
try
{
string message;
if (this.RequiresQuestionAndAnswer && string.IsNullOrEmpty(passwordQuestion))
{
userCreationStatus = MembershipCreateStatus.InvalidQuestion;
throw new ProviderException("The question provided is not a valid question.");
}
if (this.RequiresQuestionAndAnswer && string.IsNullOrEmpty(passwordAnswer))
{
userCreationStatus = MembershipCreateStatus.InvalidAnswer;
throw new ProviderException("The answer provided is not a valid answer.");
}
if (string.IsNullOrEmpty(username))
{
userCreationStatus = MembershipCreateStatus.InvalidUserName;
throw new ProviderException("The user name provided is not a valid SharePoint username.");
}
if (!this.IsStrongPassword(password, out message))
{
userCreationStatus = MembershipCreateStatus.InvalidPassword;
throw new ProviderException(message);
}
password = this.EncodePassword(password);
passwordAnswer = this.EncodePassword(passwordAnswer);
DateTime currentTime = DateTime.Now;
createUserDbConnection = this.provider.CreateConnection();
createUserDbConnection.ConnectionString = this.connectionString;
createUserDbConnection.Open();
DbTransaction membershipDbTransaction = createUserDbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
if ((userId != null) && !(userId is Guid))
{
throw new ArgumentException("userId");
}
if (userId == null)
{
userId = Guid.NewGuid();
}
this.usersProvider.CreateUser(createUserDbConnection, false, username, userId, true, out status);
if (status != MembershipCreateStatus.Success)
{
userCreationStatus = status;
return sharepointUser;
}
createUserDbCommand = this.CreateCommand("INSERT INTO aspnet_membership (email, passwordquestion, passwordanswer, isapproved, islockedout, creationdate, lastpasswordchangeddate, lastlockoutdate, userid, password) VALUES (:email, :passwordquestion, :passwordanswer, :isapproved, :islockedout, :creationdate, :lastpasswordchangeddate, :lastlockoutdate, :userid, :password)", createUserDbConnection);
createUserDbCommand.Parameters.Add(this.CreateParameter("email", DbType.String, 0xff, email));
createUserDbCommand.Parameters.Add(this.CreateParameter("passwordquestion", DbType.String, 0xff, passwordQuestion));
createUserDbCommand.Parameters.Add(this.CreateParameter("passwordanswer", DbType.String, 0xff, passwordAnswer));
createUserDbCommand.Parameters.Add(this.CreateParameter("isapproved", DbType.Boolean, isApproved));
createUserDbCommand.Parameters.Add(this.CreateParameter("islockedout", DbType.Boolean, false));
createUserDbCommand.Parameters.Add(this.CreateParameter("creationdate", DbType.DateTime, currentTime));
createUserDbCommand.Parameters.Add(this.CreateParameter("lastpasswordchangeddate", DbType.DateTime, currentTime));
createUserDbCommand.Parameters.Add(this.CreateParameter("lastlockoutdate", DbType.DateTime, currentTime));
createUserDbCommand.Parameters.Add(this.CreateParameter("userid", DbType.Guid, 0xff, userId));
createUserDbCommand.Parameters.Add(this.CreateParameter("password", DbType.String, 0xff, password));
createUserDbCommand.ExecuteNonQuery();
sharepointUser = new MembershipUser(this.Name, username, userId, email, passwordQuestion, null, isApproved, false, currentTime, currentTime, currentTime, currentTime, currentTime);
membershipDbTransaction.Commit();
}
catch (Exception exception)
{
membershipDbTransaction.Rollback();
throw exception;
}
}
catch (ProviderException exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "CreateUser");
}
if (userCreationStatus == MembershipCreateStatus.Success)
{
userCreationStatus = MembershipCreateStatus.ProviderError;
}
throw exception;
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "CreateUser");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
if (createUserDbConnection != null)
{
createUserDbConnection.Close();
}
}
status = userCreationStatus;
return sharepointUser;
}
///
/// Decore the userpassword from the custom membership store for consumption
///
///
///
internal string DecodePassword(string pass)
{
string decPass;
if (pass == null)
{
return null;
}
switch (this.passwordFormat)
{
case MembershipPasswordFormat.Clear:
return pass;
case MembershipPasswordFormat.Hashed:
throw new ProviderException("The SharePoint membership provider could not decode the hashed password.");
}
try
{
byte[] convertBuffer = Convert.FromBase64String(pass);
byte[] decryptBuffer = this.DecryptPassword(convertBuffer);
if (decryptBuffer == null)
{
return null;
}
decPass = Encoding.Unicode.GetString(decryptBuffer, 0x10, decryptBuffer.Length - 0x10);
}
catch (Exception)
{
throw new ProviderException("The SharePoint membership provider could not decode the hashed password.");
}
return decPass;
}
///
/// Delete a user from the custom membership data store
///
///
///
///
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
DbConnection deleteUserDbConnection = this.provider.CreateConnection();
deleteUserDbConnection.ConnectionString = this.connectionString;
// Confirm the deleation of the user
bool flagConfirm = false;
try
{
deleteUserDbConnection.Open();
DbTransaction deleteUserDbTransaction = deleteUserDbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
flagConfirm = this.usersProvider.DeleteUser(deleteUserDbConnection, username, SharePointUsersProvider.ProviderType.All);
deleteUserDbTransaction.Commit();
return flagConfirm;
}
catch (Exception exception)
{
deleteUserDbTransaction.Rollback();
throw exception;
}
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "DeleteUser");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
deleteUserDbConnection.Close();
}
return flagConfirm;
}
///
/// Encode the user password using a salt to hinder dictionary attacks against the custom membership data store
///
///
///
internal string EncodePassword(string pass)
{
string encPass;
try
{
byte[] secondaryBuffer;
if (pass == null)
{
return null;
}
string randomValues = this.GenerateSalt();
if (this.passwordFormat == MembershipPasswordFormat.Clear)
{
return pass;
}
byte[] primaryBuffer = Encoding.Unicode.GetBytes(pass);
byte[] encryptBuffer = null;
if (this.passwordFormat == MembershipPasswordFormat.Encrypted)
{
byte[] randomBuffer = Convert.FromBase64String(randomValues);
secondaryBuffer = new byte[randomBuffer.Length + primaryBuffer.Length];
Buffer.BlockCopy(randomBuffer, 0, secondaryBuffer, 0, randomBuffer.Length);
Buffer.BlockCopy(primaryBuffer, 0, secondaryBuffer, randomBuffer.Length, primaryBuffer.Length);
encryptBuffer = this.EncryptPassword(secondaryBuffer);
}
else
{
secondaryBuffer = new byte[primaryBuffer.Length];
Buffer.BlockCopy(primaryBuffer, 0, secondaryBuffer, 0, primaryBuffer.Length);
HashAlgorithm encodeAlgorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
if (encodeAlgorithm == null)
{
throw new ProviderException("Undefined Hash Algorithm Provided");
}
encryptBuffer = encodeAlgorithm.ComputeHash(secondaryBuffer);
}
encPass = Convert.ToBase64String(encryptBuffer);
}
catch (Exception exception)
{
if (exception is ProviderException)
{
throw exception;
}
throw new ProviderException("The SharePoint membership provider could not encode the password.");
}
return encPass;
}
///
/// Find all the users by matching their email addresses
///
///
///
///
///
///
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
if (string.IsNullOrEmpty(emailToMatch))
{
throw new ArgumentNullException("emailToMatch");
}
MembershipUserCollection userCollection = this.QueryAllUsers("email", emailToMatch.ToLower(), pageIndex, pageSize, out totalRecords, false);
totalRecords = userCollection.Count;
return userCollection;
}
///
/// Find users by their specified names
///
///
///
///
///
///
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
if (string.IsNullOrEmpty(usernameToMatch))
{
throw new ArgumentNullException("usernameToMatch");
}
MembershipUserCollection userCollection = this.QueryAllUsers("username", usernameToMatch.ToLower(), pageIndex, pageSize, out totalRecords, false);
totalRecords = userCollection.Count;
return userCollection;
}
///
/// Generate the salt for encrypting the password with random characters to hinder dictionary attacks
///
///
internal string GenerateSalt()
{
byte[] saltBuffer = new byte[0x10];
new RNGCryptoServiceProvider().GetBytes(saltBuffer);
return Convert.ToBase64String(saltBuffer);
}
///
/// Get all the users
///
///
///
///
///
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
return this.QueryAllUsers(null, null, pageIndex, pageSize, out totalRecords, true);
}
///
/// Get all the failed attempts against the custom membership datastore
///
///
///
///
private bool GetFailedAttempts(string userName, bool normalLogin)
{
string dbAttempt;
DbConnection failedAttemptConnection = this.provider.CreateConnection();
failedAttemptConnection.ConnectionString = this.connectionString;
if (normalLogin)
{
dbAttempt = "SELECT m.failedpasswordattemptcount, m.failedpasswordattemptstart FROM aspnet_membership m, aspnet_users u WHERE u.userid = m.userid AND LOWER(u.applicationname) = :applicationname AND LOWER(u.UserName) = :username";
}
else
{
dbAttempt = "SELECT m.failedpasswordanswercount, m.failedpasswordanswerstart FROM aspnet_membership m, aspnet_users u WHERE u.userid = m.userid AND LOWER(u.applicationname) = :applicationname AND LOWER(u.UserName) = :username";
}
IDbCommand failedAttemptCommand = this.CreateCommand(dbAttempt, failedAttemptConnection);
failedAttemptCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower()));
failedAttemptCommand.Parameters.Add(this.CreateParameter("username", DbType.String, 0xff, userName.ToLower()));
int metric = 0;
DateTime currentTime = new DateTime();
try
{
failedAttemptConnection.Open();
IDataReader failedAttemptReader = failedAttemptCommand.ExecuteReader();
if (failedAttemptReader.Read())
{
metric = failedAttemptReader.GetInt32(0);
currentTime = failedAttemptReader.GetDateTime(1);
}
failedAttemptReader.Close();
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "GetFailedAttempts");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
failedAttemptConnection.Close();
}
bool flagLogin = true;
if (currentTime <= (DateTime.Now - new TimeSpan(this.PasswordAttemptWindow * 0x23c34600)))
{
metric = 0;
}
else if (metric >= (this.MaxInvalidPasswordAttempts - 1))
{
flagLogin = false;
}
if (normalLogin)
{
this.UpdateMembershipParameter(userName, new object[] { "failedpasswordattemptcount", DbType.Int32, metric + 1, "failedpasswordattemptstart", DbType.DateTime, DateTime.Now });
return flagLogin;
}
this.UpdateMembershipParameter(userName, new object[] { "failedpasswordanswercount", DbType.Int32, metric + 1, "failedpasswordanswerstart", DbType.DateTime, DateTime.Now });
return flagLogin;
}
///
/// Get memberships based on the passed username from the custom membership datastore
///
///
///
///
///
///
///
private MembershipUser GetMembershipByUserName(string parameterName, DbType dbType, object value, bool useLike, bool caseSensitive)
{
DbConnection getMemByUserNameConnection = this.provider.CreateConnection();
getMemByUserNameConnection.ConnectionString = this.connectionString;
string userInfo = "SELECT username, u.userid, email, passwordquestion, comments, isapproved, islockedout, creationdate, lastlogindate, lastactivitydate, lastpasswordchangeddate, lastlockoutdate FROM aspnet_membership m, aspnet_users u WHERE u.userid=m.userid AND LOWER(u.applicationname) = :applicationname AND ";
if (parameterName == "userid")
{
userInfo = userInfo + "u.";
}
if (caseSensitive)
{
if (useLike)
{
userInfo = userInfo + parameterName + " LIKE :" + parameterName;
}
else
{
userInfo = userInfo + parameterName + " = :" + parameterName;
}
}
else if (useLike)
{
userInfo = userInfo + "LOWER(" + parameterName + ") LIKE :" + parameterName;
}
else
{
userInfo = userInfo + "LOWER(" + parameterName + ") = :" + parameterName;
}
IDbCommand getMemByUserNameCommand = this.CreateCommand(userInfo, getMemByUserNameConnection);
getMemByUserNameCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower()));
getMemByUserNameCommand.Parameters.Add(this.CreateParameter(parameterName, dbType, value));
MembershipUser sharepointUser = null;
try
{
getMemByUserNameConnection.Open();
IDataReader getMemByUserNameReader = getMemByUserNameCommand.ExecuteReader();
if (getMemByUserNameReader.Read())
{
string userName = getMemByUserNameReader.GetString(0);
Guid nameGUID = (Guid) this.ReadField(getMemByUserNameReader, 1, DbType.Guid);
string email = getMemByUserNameReader.GetString(2);
string passQues = getMemByUserNameReader.GetString(3);
string comments = getMemByUserNameReader.GetString(4);
bool flagApprove = getMemByUserNameReader.GetBoolean(5);
bool flagLook = getMemByUserNameReader.GetBoolean(6);
DateTime creationDate = getMemByUserNameReader.GetDateTime(7);
DateTime lastLoginDate = getMemByUserNameReader.GetDateTime(8);
DateTime lastActiveDate = getMemByUserNameReader.GetDateTime(9);
DateTime lastPassChange = getMemByUserNameReader.GetDateTime(10);
DateTime lastLook = getMemByUserNameReader.GetDateTime(11);
sharepointUser = new MembershipUser(this.Name, userName, nameGUID, email, passQues, comments, flagApprove, flagLook, creationDate, lastLoginDate, lastActiveDate, lastPassChange, lastLook);
}
getMemByUserNameReader.Close();
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "GetUserMembershipByUserName");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
getMemByUserNameConnection.Close();
}
return sharepointUser;
}
///
/// Get the number of users that are online from the membership data store
///
///
public override int GetNumberOfUsersOnline()
{
DbConnection numUserOnlineConnection = this.provider.CreateConnection();
numUserOnlineConnection.ConnectionString = this.connectionString;
IDbCommand numUserOnlineCommand = this.CreateCommand("SELECT COUNT(*) FROM aspnet_users WHERE LOWER(applicationname) = :applicationname AND lastactivitydate >= :lastactivitydate1 AND lastactivitydate <= :lastactivitydate2", numUserOnlineConnection);
numUserOnlineCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower()));
DateTime curentTime = DateTime.Now;
numUserOnlineCommand.Parameters.Add(this.CreateParameter("lastactivitydate1", DbType.DateTime, curentTime - new TimeSpan(Membership.UserIsOnlineTimeWindow * 0x23c34600)));
numUserOnlineCommand.Parameters.Add(this.CreateParameter("lastactivitydate2", DbType.DateTime, curentTime));
int metric = 0;
try
{
numUserOnlineConnection.Open();
IDataReader numUserOnlineReader = numUserOnlineCommand.ExecuteReader();
if (numUserOnlineReader.Read())
{
metric = numUserOnlineReader.GetInt32(0);
}
numUserOnlineReader.Close();
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "GetNumberOfUsersOnline");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
numUserOnlineConnection.Close();
}
return metric;
}
///
/// Get the password of a user based on the passed username from the custom membership data store
///
///
///
///
public override string GetPassword(string username, string answer)
{
if (!this.EnablePasswordRetrieval)
{
throw new NotSupportedException();
}
if (this.PasswordFormat == MembershipPasswordFormat.Hashed)
{
throw new ProviderException("The SharePoint membership provider could not retrieve hashed passwords.");
}
this.GetUserMembershipByUserName(username);
return this.CheckUsersAnswer(username, answer, true);
}
///
/// Get a specific user by passing in the userID
///
///
///
///
public override MembershipUser GetUser(object userId, bool userIsOnline)
{
if (userId == null)
{
throw new ArgumentNullException("providerUserKey");
}
if (!(userId is Guid))
{
throw new ArgumentException("invalid providerUserKey");
}
MembershipUser sharepointUser = this.GetMembershipByUserName("userid", DbType.Guid, userId, false, true);
if ((sharepointUser != null) && userIsOnline)
{
this.usersProvider.UpdateUserParameter(this.connectionString, sharepointUser.UserName, new object[] { "lastactivitydate", DbType.DateTime, DateTime.Now });
}
return sharepointUser;
}
///
/// Get a user from the custom membership data store
///
///
///
///
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (string.IsNullOrEmpty(username))
{
throw new ArgumentNullException("username");
}
MembershipUser sharepointUser = this.GetMembershipByUserName("username", DbType.String, username.ToLower(), false, false);
if ((sharepointUser != null) && userIsOnline)
{
this.usersProvider.UpdateUserParameter(this.connectionString, username, new object[] { "lastactivitydate", DbType.DateTime, DateTime.Now });
}
return sharepointUser;
}
///
/// Get the user membership by passing in the specified username
///
///
///
private UserData GetUserMembershipByUserName(string username)
{
DbConnection getUseByMemConnection = this.provider.CreateConnection();
getUseByMemConnection.ConnectionString = this.connectionString;
IDbCommand getUseByMemCommand = this.CreateCommand("SELECT password, u.userid, email, passwordquestion, passwordanswer, comments, isapproved, islockedout, creationdate, lastlogindate, lastactivitydate, lastpasswordchangeddate, lastlockoutdate FROM aspnet_membership m, aspnet_users u WHERE u.userid=m.userid AND LOWER(u.applicationname) = :applicationname AND LOWER(u.username) = :username", getUseByMemConnection);
getUseByMemCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower()));
getUseByMemCommand.Parameters.Add(this.CreateParameter("username", DbType.String, 0xff, username.ToLower()));
UserData userInformation = null;
try
{
getUseByMemConnection.Open();
IDataReader getUseByMemReader = getUseByMemCommand.ExecuteReader();
if (getUseByMemReader.Read())
{
string pass = getUseByMemReader.GetString(0);
Guid userID = (Guid) this.ReadField(getUseByMemReader, 1, DbType.Guid);
string email = getUseByMemReader.GetString(2);
string passQuestion = getUseByMemReader.GetString(3);
string passAnswer = getUseByMemReader.GetString(4);
string comments = getUseByMemReader.GetString(5);
bool approve = getUseByMemReader.GetBoolean(6);
bool lockedOut = getUseByMemReader.GetBoolean(7);
DateTime creationDate = getUseByMemReader.GetDateTime(8);
DateTime lastLoginDate = getUseByMemReader.GetDateTime(9);
DateTime lastActivityDate = getUseByMemReader.GetDateTime(10);
DateTime lastPasswordChangeDate = getUseByMemReader.GetDateTime(11);
DateTime lastLockoutDate = getUseByMemReader.GetDateTime(12);
MembershipUser sharepointUser = new MembershipUser(this.Name, username, userID, email, passQuestion, comments, approve, lockedOut, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangeDate, lastLockoutDate);
userInformation = new UserData(sharepointUser, pass, passAnswer);
}
getUseByMemReader.Close();
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "GetUserMembershipByUserName");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
getUseByMemConnection.Close();
}
return userInformation;
}
///
/// Get the specified username by passing in a specific email
///
///
///
public override string GetUserNameByEmail(string email)
{
if (string.IsNullOrEmpty(email))
{
throw new ArgumentNullException("email");
}
MembershipUser sharepointUser = this.GetMembershipByUserName("email", DbType.String, email.ToLower(), false, false);
if (sharepointUser == null)
{
return "";
}
return sharepointUser.UserName;
}
///
/// Call the custom DbUsersProvider class
///
///
internal abstract SharePointUsersProvider GetUsersProvider();
///
/// Intialization based on configuration setttings
///
///
///
public override void Initialize(string name, NameValueCollection config)
{
if (config == null)
{
throw new ArgumentNullException("config");
}
if (string.IsNullOrEmpty(name))
{
name = this.defaultName;
}
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", this.defaultDescription);
}
base.Initialize(name, config);
string pass = config["passwordFormat"];
if (string.IsNullOrEmpty(pass))
{
pass = "Hashed";
}
if (pass == "Encrypted")
{
this.passwordFormat = MembershipPasswordFormat.Encrypted;
}
else if (pass == "Hashed")
{
this.passwordFormat = MembershipPasswordFormat.Hashed;
}
else
{
if (pass != "Clear")
{
throw new ProviderException("SharePoint membership provider bad password format has been specified");
}
this.passwordFormat = MembershipPasswordFormat.Clear;
}
config.Remove("passwordFormat");
this._EnablePasswordRetrieval = GeneralUtilities.GetBooleanValue(config, "enablePasswordRetrieval", false);
config.Remove("enablePasswordRetrieval");
if ((this.PasswordFormat == MembershipPasswordFormat.Hashed) && this._EnablePasswordRetrieval)
{
throw new ProviderException("Cannot retrieve Hashed passwords.");
}
this._EnablePasswordReset = GeneralUtilities.GetBooleanValue(config, "enablePasswordReset", true);
config.Remove("enablePasswordReset");
this._RequiresQuestionAndAnswer = GeneralUtilities.GetBooleanValue(config, "requiresQuestionAndAnswer", true);
config.Remove("requiresQuestionAndAnswer");
this._RequiresUniqueEmail = GeneralUtilities.GetBooleanValue(config, "requiresUniqueEmail", true);
config.Remove("requiresUniqueEmail");
this._MaxInvalidPasswordAttempts = GeneralUtilities.GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 100);
config.Remove("maxInvalidPasswordAttempts");
this._PasswordAttemptWindow = GeneralUtilities.GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
config.Remove("passwordAttemptWindow");
this._MinRequiredPasswordLength = GeneralUtilities.GetIntValue(config, "minRequiredPasswordLength", 7, false, 0x80);
config.Remove("minRequiredPasswordLength");
this._MinRequiredNonalphanumericCharacters = GeneralUtilities.GetIntValue(config, "minRequiredNonalphanumericCharacters", 1, true, 0x80);
config.Remove("minRequiredNonalphanumericCharacters");
this.pApplicationName = config["ApplicationName"];
if (string.IsNullOrEmpty(this.pApplicationName))
{
this.pApplicationName = HostingEnvironment.ApplicationVirtualPath;
}
config.Remove("ApplicationName");
this.pConnectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
config.Remove("connectionStringName");
if (this.pConnectionStringSettings == null)
{
this.connectionString = "";
}
else
{
this.connectionString = this.pConnectionStringSettings.ConnectionString.Trim();
}
if (config.Count > 0)
{
string unrecongizedAtt = config.GetKey(0);
if (!string.IsNullOrEmpty(unrecongizedAtt))
{
throw new ProviderException("Arbitrary unrecognizable attribute related to the SharePoint membership provider: " + unrecongizedAtt);
}
}
this.usersProvider = this.GetUsersProvider();
}
///
/// Determine the strength of a password and whether it satisified stated conditions
///
///
///
///
private bool IsStrongPassword(string newPassword, out string errorMessage)
{
errorMessage = null;
if ((newPassword == null) || (newPassword.Length < this.MinRequiredPasswordLength))
{
errorMessage = "The provided SharePoint member password is shorter than" + this.MinRequiredPasswordLength.ToString(CultureInfo.InvariantCulture);
return false;
}
int metric = 0;
for (int i = 0; i < newPassword.Length; i++)
{
if (!char.IsLetterOrDigit(newPassword, i))
{
metric++;
}
}
if (metric < this.MinRequiredNonAlphanumericCharacters)
{
errorMessage = "The provided SharePoint member password requires non-alphanumeric characters greater than " + this.MinRequiredNonAlphanumericCharacters.ToString(CultureInfo.InvariantCulture);
return false;
}
if ((this.PasswordStrengthRegularExpression.Length > 0) && !Regex.IsMatch(newPassword, this.PasswordStrengthRegularExpression))
{
errorMessage = "The provided SharePoint member password does not appropriatly match regular expression";
return false;
}
if (newPassword.Length > 0xff)
{
errorMessage = "The provided SharePoint member password is too long";
return false;
}
return true;
}
private MembershipUserCollection QueryAllUsers(string stringParameterName, string likeExpression, int pageIndex, int pageSize, out int totalRecords, bool caseSensitive)
{
MembershipUserCollection userCollection = new MembershipUserCollection();
DbConnection queryUsersConnection = this.provider.CreateConnection();
queryUsersConnection.ConnectionString = this.connectionString;
string queryText = "SELECT username, password, u.userid, email, passwordquestion, passwordanswer, comments, isapproved, islockedout, creationdate, lastlogindate, lastactivitydate, lastpasswordchangeddate, lastlockoutdate FROM aspnet_membership m, aspnet_users u WHERE u.userid = m.userid AND LOWER(u.applicationname) = :applicationname";
if (stringParameterName != null)
{
if (caseSensitive)
{
queryText = queryText + " AND " + stringParameterName + " LIKE :" + stringParameterName;
}
else
{
queryText = queryText + " AND LOWER(" + stringParameterName + ") LIKE :" + stringParameterName;
}
}
IDbCommand queryUsersCommand = this.CreateCommand(queryText, queryUsersConnection);
queryUsersCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower()));
if (stringParameterName != null)
{
queryUsersCommand.Parameters.Add(this.CreateParameter(stringParameterName, DbType.String, likeExpression));
}
try
{
queryUsersConnection.Open();
IDataReader queryUsersReader = queryUsersCommand.ExecuteReader();
while (queryUsersReader.Read())
{
string userName = queryUsersReader.GetString(0);
queryUsersReader.GetString(1);
Guid userID = (Guid) this.ReadField(queryUsersReader, 2, DbType.Guid);
string email = queryUsersReader.GetString(3);
string passQuestion = queryUsersReader.GetString(4);
queryUsersReader.GetString(5);
string comments = queryUsersReader.GetString(6);
bool approval = queryUsersReader.GetBoolean(7);
bool lockedOut = queryUsersReader.GetBoolean(8);
DateTime creationDate = queryUsersReader.GetDateTime(9);
DateTime lastLoginDate = queryUsersReader.GetDateTime(10);
DateTime lastActivityDate = queryUsersReader.GetDateTime(11);
DateTime lastPassChangeDate = queryUsersReader.GetDateTime(12);
DateTime lastLockOutDate = queryUsersReader.GetDateTime(13);
MembershipUser sharepointUser = new MembershipUser(this.Name, userName, userID, email, passQuestion, comments, approval, lockedOut, creationDate, lastLoginDate, lastActivityDate, lastPassChangeDate, lastLockOutDate);
userCollection.Add(sharepointUser);
}
queryUsersReader.Close();
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "GetAllUsers");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
queryUsersConnection.Close();
}
totalRecords = userCollection.Count;
return userCollection;
}
protected virtual object ReadField(IDataReader reader, int i, DbType dbType)
{
return reader.GetValue(i);
}
public override string ResetPassword(string username, string answer)
{
if (!this.EnablePasswordReset)
{
throw new NotSupportedException();
}
this.CheckUsersAnswer(username, answer, false);
string newPassword = Membership.GeneratePassword(this.MinRequiredPasswordLength, this.MinRequiredNonAlphanumericCharacters);
this.UpdateMembershipParameter(username, new object[] { "lastpasswordchangeddate", DbType.DateTime, DateTime.Now, "password", DbType.String, this.EncodePassword(newPassword) });
return newPassword;
}
///
/// Unlock the user based on the passed in username
///
///
///
public override bool UnlockUser(string userName)
{
this.UpdateMembershipParameter(userName, new object[] { "islockedout", DbType.Boolean, false, "failedpasswordanswercount", DbType.Int32, 0, "failedpasswordattemptcount", DbType.Int32, 0 });
return true;
}
///
/// Update various portions of the membership parameters
///
///
///
private void UpdateMembershipParameter(string userName, params object[] parameters)
{
DbConnection updateMemDbConnection = this.provider.CreateConnection();
updateMemDbConnection.ConnectionString = this.connectionString;
try
{
updateMemDbConnection.Open();
DbTransaction updateMemDbTransaction = updateMemDbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
bool dummyVar;
object userObject = null;
userObject = this.usersProvider.GetMembershipByUserName(updateMemDbConnection, "username", DbType.String, userName.ToLower(), out dummyVar, false);
if (userObject != null)
{
string textQuery = "UPDATE aspnet_membership SET ";
for (int i = 0; i < parameters.Length; i += 3)
{
textQuery = textQuery + ((string) parameters[i]) + " = :" + ((string) parameters[i]);
if ((i + 3) < parameters.Length)
{
textQuery = textQuery + ", ";
}
else
{
textQuery = textQuery + " ";
}
}
textQuery = textQuery + "WHERE userid = :userid";
IDbCommand updateMemDbCommand = this.CreateCommand(textQuery, updateMemDbConnection);
for (int i = 0; i < parameters.Length; i += 3)
{
updateMemDbCommand.Parameters.Add(this.CreateParameter((string) parameters[i], (DbType) parameters[i + 1], parameters[i + 2]));
}
updateMemDbCommand.Parameters.Add(this.CreateParameter("userid", DbType.Guid, userObject));
updateMemDbCommand.ExecuteNonQuery();
updateMemDbTransaction.Commit();
}
}
catch (Exception exception)
{
updateMemDbTransaction.Rollback();
throw exception;
}
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "UpdateMembershipParameter");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
updateMemDbConnection.Close();
}
}
///
/// Update the user
///
///
public override void UpdateUser(MembershipUser user)
{
DbConnection updateUserDbConnection = this.provider.CreateConnection();
updateUserDbConnection.ConnectionString = this.connectionString;
try
{
updateUserDbConnection.Open();
DbTransaction updateUserDbTransaction = updateUserDbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
Guid userID = this.usersProvider.UpdateUser(updateUserDbConnection, user);
IDbCommand updateUserDbCommand = this.CreateCommand("UPDATE aspnet_membership SET email = :email, comments = :comments, isapproved = :isapproved WHERE userid = :userid", updateUserDbConnection);
updateUserDbCommand.Parameters.Add(this.CreateParameter("email", DbType.String, 0xff, user.Email));
updateUserDbCommand.Parameters.Add(this.CreateParameter("comments", DbType.String, 0xff, user.Comment));
updateUserDbCommand.Parameters.Add(this.CreateParameter("isapproved", DbType.Boolean, user.IsApproved));
updateUserDbCommand.Parameters.Add(this.CreateParameter("userid", DbType.Guid, 0xff, userID));
updateUserDbCommand.ExecuteNonQuery();
updateUserDbTransaction.Commit();
}
catch (Exception exception)
{
updateUserDbTransaction.Rollback();
throw exception;
}
}
catch (Exception exception)
{
if (this.WriteExceptionsToEventLog)
{
this.WriteToEventLog(exception, "UpdateUser");
}
throw new ProviderException(this.exceptionMessage, exception);
}
finally
{
updateUserDbConnection.Close();
}
}
///
/// Validate the user
///
///
///
///
public override bool ValidateUser(string username, string password)
{
bool flagValid = this.CheckUsersPassword(username, password);
if (flagValid)
{
this.UpdateMembershipParameter(username, new object[] { "lastlogindate", DbType.DateTime, DateTime.Now, "failedpasswordattemptcount", DbType.Int32, 0 });
}
return flagValid;
}
///
/// Write an errors to the event log
///
///
///
private void WriteToEventLog(Exception e, string action)
{
EventLog log = new EventLog();
log.Source = this.defaultName;
log.Log = this.eventLog;
string message = "An exception occurred communicating with the SharePoint membership store.\n\n";
message = message + "Action: " + action + "\n\n";
message = message + "Exception: " + e.ToString();
log.WriteEntry(message);
}
}
}