|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Collections.Specialized; |
|
using System.Configuration.Provider; |
|
using System.Linq; |
|
using System.Web.Security; |
|
using Telerik.Sitefinity.Data; |
|
using Telerik.Sitefinity.Localization; |
|
using Telerik.Sitefinity.Model; |
|
using Telerik.Sitefinity.Security; |
|
using Telerik.Sitefinity.Security.Data; |
|
using Telerik.Sitefinity.Security.Model; |
|
using SitefinityWebApp.Model; |
|
|
|
namespace SitefinityWebApp.Providers |
|
{ |
|
/// <summary> |
|
/// Custom Membership Provider class |
|
/// </summary> |
|
public class CustomMembershipProvider : MembershipDataProvider |
|
{ |
|
#region Privates variables |
|
private ManagerInfo managerInfo; |
|
private string providerName = "CustomMembershipProvider"; |
|
private DateTime minDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
|
#endregion |
|
|
|
#region Public variables |
|
public const string ServiceEndpointKey = "serviceEndpoint"; |
|
#endregion |
|
|
|
#region Properties |
|
|
|
public CustomMembershipProviderEntities ProviderEntities { get; set; } |
|
|
|
public string ProviderName |
|
{ |
|
get { return this.providerName; } |
|
set { this.providerName = value; } |
|
} |
|
|
|
public override string ApplicationName |
|
{ |
|
get |
|
{ |
|
return "/CustomMembershipProvider"; |
|
} |
|
} |
|
|
|
private ManagerInfo ManagerInfo |
|
{ |
|
get |
|
{ |
|
return this.managerInfo ?? (this.managerInfo = new ManagerInfo() |
|
{ |
|
ProviderName = this.ProviderName, |
|
ManagerType = "Telerik.Sitefinity.Security.UserManager", |
|
ApplicationName = this.ApplicationName |
|
}); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the provider abilities for the current principal. E.g. which operations are supported and allowed |
|
/// </summary> |
|
/// <value>The provider abilities.</value> |
|
public override ProviderAbilities Abilities |
|
{ |
|
get |
|
{ |
|
var abilities = new ProviderAbilities { ProviderName = Name, ProviderType = GetType().FullName }; |
|
abilities.AddAbility("GetUser", true, true); |
|
abilities.AddAbility("AddUser", true, true); |
|
abilities.AddAbility("DeleteUser", true, true); |
|
abilities.AddAbility("UpdateUser", true, true); |
|
abilities.AddAbility("ValidateUser", true, true); |
|
abilities.AddAbility("ResetPassword", true, true); |
|
abilities.AddAbility("GetPassword", true, true); |
|
return abilities; |
|
} |
|
} |
|
|
|
#endregion |
|
|
|
#region Methods |
|
/// <summary> |
|
/// Create a new transaction |
|
/// </summary> |
|
/// <param name="transactionName"></param> |
|
/// <returns></returns> |
|
protected override object CreateNewTransaction(string transactionName) |
|
{ |
|
return new object(); |
|
} |
|
|
|
/// <summary> |
|
/// Commit the transaction |
|
/// </summary> |
|
public override void CommitTransaction() |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Get items |
|
/// </summary> |
|
/// <returns></returns> |
|
public override IEnumerable GetItems(Type itemType, string filterExpression, string orderExpression, int skip, int take, ref int? totalCount) |
|
{ |
|
return base.GetItems(itemType, filterExpression, orderExpression, skip, take, ref totalCount); |
|
} |
|
|
|
/// <summary> |
|
/// Get a new Guid |
|
/// </summary> |
|
/// <returns></returns> |
|
protected override Guid GetNewGuid() |
|
{ |
|
return Guid.NewGuid(); |
|
} |
|
|
|
/// <summary> |
|
/// Dispose |
|
/// </summary> |
|
/// <param name="disposing"></param> |
|
protected override void Dispose(bool disposing) |
|
{ |
|
if (!disposing) |
|
{ |
|
base.Dispose(false); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Check if the password is valid |
|
/// </summary> |
|
/// <param name="user"></param> |
|
/// <param name="password"></param> |
|
/// <returns></returns> |
|
private bool CheckValidPassword(User user, string password) |
|
{ |
|
if (user == null || !user.IsApproved || string.IsNullOrWhiteSpace(user.Password)) |
|
return false; |
|
|
|
if (user.IsLockedOut) |
|
{ |
|
var failedPasswordAttemptWindowStart = user.FailedPasswordAttemptWindowStart; |
|
if (DateTime.UtcNow <= failedPasswordAttemptWindowStart.AddMinutes((double)this.PasswordAttemptWindow)) |
|
{ |
|
return false; |
|
} |
|
|
|
this.UnlockUser(user); |
|
} |
|
|
|
var passwordFormat = (MembershipPasswordFormat)user.PasswordFormat; |
|
return this.CheckValidPassword(string.Concat(password, user.Salt), user.Password, passwordFormat); |
|
} |
|
|
|
/// <summary> |
|
/// Check if the password is valid |
|
/// </summary> |
|
/// <param name="enteredByUser"></param> |
|
/// <param name="original"></param> |
|
/// <param name="passwordFormat"></param> |
|
/// <returns></returns> |
|
private bool CheckValidPassword(string enteredByUser, string original, MembershipPasswordFormat passwordFormat) |
|
{ |
|
if (passwordFormat != MembershipPasswordFormat.Encrypted) |
|
return original == this.EncodePassword(enteredByUser, null, passwordFormat); |
|
|
|
var str = DecodePassword(original, passwordFormat); |
|
return str == enteredByUser; |
|
} |
|
|
|
/// <summary> |
|
/// Check if the EmailAddress already exits |
|
/// </summary> |
|
/// <param name="email"></param> |
|
/// <returns></returns> |
|
public override bool EmailExists(string email) |
|
{ |
|
return this.ProviderEntities.Customers.SingleOrDefault(c => c.Email == email) != null; |
|
} |
|
|
|
/// <summary> |
|
/// Check if the Username already exits |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <returns></returns> |
|
public override bool UserExists(string userName) |
|
{ |
|
return this.ProviderEntities.Customers.SingleOrDefault(c => c.Username == userName) != null; |
|
} |
|
|
|
/// <summary> |
|
/// Create a new Sitefinity User based on our Customer entity. |
|
/// This method is being used by: GetUsers(), GetUser() |
|
/// </summary> |
|
/// <param name="customer"></param> |
|
/// <returns></returns> |
|
private User GetSitefinityUser(Customer customer) |
|
{ |
|
var user = new User |
|
{ |
|
ApplicationName = this.ApplicationName, |
|
IsBackendUser = false, |
|
Id = customer.CustomerId, |
|
Email = customer.Email, |
|
Comment = string.Empty, |
|
LastActivityDate = DateTime.UtcNow.AddDays(-1), |
|
LastModified = minDate, |
|
LastLoginDate = minDate, |
|
FailedPasswordAnswerAttemptWindowStart = minDate, |
|
FailedPasswordAttemptWindowStart = minDate, |
|
Password = customer.Password, |
|
Salt = customer.Salt, |
|
ManagerInfo = ManagerInfo, |
|
IsApproved = customer.IsApproved, |
|
PasswordFormat = 2 |
|
}; |
|
|
|
user.SetUserName(customer.Username); |
|
user.SetCreationDate(DateTime.Now); |
|
user.SetIsLockedOut(false); |
|
user.SetLastLockoutDate(DateTime.Now); |
|
user.SetLastPasswordChangedDate(DateTime.Now); |
|
user.SetPasswordQuestion("question"); |
|
|
|
return user; |
|
} |
|
|
|
/// <summary> |
|
/// Initializing |
|
/// </summary> |
|
/// <param name="providerName"></param> |
|
/// <param name="config"></param> |
|
/// <param name="managerType"></param> |
|
protected override void Initialize(string providerName, NameValueCollection config, Type managerType) |
|
{ |
|
// Initialize a new instance of our Membership Provider Entity model |
|
this.ProviderEntities = new CustomMembershipProviderEntities(); |
|
base.Initialize(providerName, config, managerType); |
|
} |
|
|
|
/// <summary> |
|
/// Get Users |
|
/// </summary> |
|
/// <returns></returns> |
|
public override IQueryable<User> GetUsers() |
|
{ |
|
var customers = this.ProviderEntities.Customers; |
|
var users = new List<User>(); |
|
|
|
foreach (var customer in customers) |
|
{ |
|
var user = this.GetSitefinityUser(customer); |
|
users.Add(user); |
|
} |
|
|
|
return users.AsQueryable(); |
|
} |
|
|
|
/// <summary> |
|
/// Get User by ID |
|
/// </summary> |
|
/// <param name="id"></param> |
|
/// <returns></returns> |
|
public override User GetUser(Guid id) |
|
{ |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.CustomerId == id); |
|
return customer == null ? null : this.GetSitefinityUser(customer); |
|
} |
|
|
|
/// <summary> |
|
/// Get User by Email |
|
/// </summary> |
|
/// <param name="email"></param> |
|
/// <returns></returns> |
|
public override User GetUserByEmail(string email) |
|
{ |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.Email == email); |
|
return customer == null ? null : this.GetSitefinityUser(customer); |
|
} |
|
|
|
/// <summary> |
|
/// Get User by Username |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <returns></returns> |
|
public override User GetUser(string userName) |
|
{ |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.Username == userName); |
|
return customer == null ? null : this.GetSitefinityUser(customer); |
|
} |
|
|
|
/// <summary> |
|
/// Create a new User. |
|
/// </summary> |
|
/// <param name="username"></param> |
|
/// <param name="password"></param> |
|
/// <param name="email"></param> |
|
/// <param name="passwordQuestion"></param> |
|
/// <param name="passwordAnswer"></param> |
|
/// <param name="isApproved"></param> |
|
/// <param name="providerUserKey"></param> |
|
/// <param name="status"></param> |
|
/// <returns></returns> |
|
public override User CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) |
|
{ |
|
// First we validate the given parameters |
|
if (!this.ValidateParameters(ref username, ref password, ref email, ref passwordQuestion, ref passwordAnswer, ref providerUserKey, out status)) |
|
{ |
|
return null; |
|
} |
|
|
|
// Generate a new Salt |
|
var str = GenerateSalt(); |
|
|
|
// Get a UTC DateTime |
|
var utcNow = DateTime.UtcNow; |
|
|
|
// Create a new user with the parameters entered from the Sitefinity backend |
|
var empty = this.CreateUser((Guid)providerUserKey, username); |
|
empty.Password = this.EncodePassword(password, str, this.PasswordFormat); |
|
empty.PasswordAnswer = this.EncodePassword(passwordAnswer.ToUpperInvariant(), null, this.PasswordFormat); |
|
empty.Salt = str; |
|
empty.Email = email; |
|
empty.Comment = string.Empty; |
|
empty.IsApproved = isApproved; |
|
empty.FailedPasswordAttemptCount = 0; |
|
empty.FailedPasswordAttemptWindowStart = utcNow; |
|
empty.FailedPasswordAnswerAttemptCount = 0; |
|
empty.FailedPasswordAnswerAttemptWindowStart = utcNow; |
|
empty.PasswordFormat = (int)PasswordFormat; |
|
empty.PasswordAnswer = passwordAnswer; |
|
empty.SetPasswordQuestion(passwordQuestion); |
|
empty.SetCreationDate(DateTime.Now); |
|
|
|
// Add the new user to the customer table in our custom database |
|
var customer = new Customer |
|
{ |
|
CustomerId = empty.Id, |
|
Username = empty.UserName, |
|
Password = empty.Password, |
|
Salt = empty.Salt, |
|
IsApproved = empty.IsApproved, |
|
Email = empty.Email |
|
}; |
|
|
|
// Save our changes |
|
this.ProviderEntities.Customers.Add(customer); |
|
this.ProviderEntities.SaveChanges(); |
|
|
|
// Return our new Sitefinity User |
|
return empty; |
|
} |
|
|
|
/// <summary> |
|
/// Create a new User |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <returns></returns> |
|
public override User CreateUser(string userName) |
|
{ |
|
return this.CreateUser(this.GetNewGuid(), userName); |
|
} |
|
|
|
/// <summary> |
|
/// Create a new User |
|
/// </summary> |
|
/// <param name="id"></param> |
|
/// <param name="userName"></param> |
|
/// <returns></returns> |
|
public override User CreateUser(Guid id, string userName) |
|
{ |
|
if (id == Guid.Empty) throw new ArgumentNullException("id"); |
|
|
|
if (!string.IsNullOrEmpty(userName)) |
|
{ |
|
LoginUtils.CheckParameter(userName, true, true, true, 256, "userName"); |
|
if (this.UserExists(userName)) |
|
{ |
|
throw new ProviderException("Username already exists."); |
|
} |
|
} |
|
|
|
var user = new User { ApplicationName = this.ApplicationName, Id = id }; |
|
user.SetUserName(userName); |
|
((IDataItem)user).Provider = this; |
|
user.ManagerInfo = ManagerInfo; |
|
|
|
return user; |
|
} |
|
|
|
/// <summary> |
|
/// Validate User |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <param name="password"></param> |
|
/// <returns></returns> |
|
public override bool ValidateUser(string userName, string password) |
|
{ |
|
return this.ValidateUser(this.GetUser(userName), password); |
|
} |
|
|
|
/// <summary> |
|
/// Validate User |
|
/// </summary> |
|
/// <param name="user"></param> |
|
/// <param name="password"></param> |
|
/// <returns></returns> |
|
public override bool ValidateUser(User user, string password) |
|
{ |
|
if (user == null) return false; |
|
|
|
var flag = this.CheckValidPassword(user, password); |
|
if (flag) |
|
{ |
|
user.LastLoginDate = DateTime.UtcNow; |
|
user.FailedPasswordAttemptWindowStart = DateTime.UtcNow; |
|
user.FailedPasswordAttemptCount = 0; |
|
} |
|
else |
|
{ |
|
this.UpdateFailureCount(user, "password"); |
|
} |
|
|
|
return flag; |
|
} |
|
|
|
/// <summary> |
|
/// Delete User |
|
/// </summary> |
|
/// <param name="item"></param> |
|
public override void Delete(User item) |
|
{ |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.Username == item.UserName); |
|
if (customer == null) return; |
|
|
|
this.ProviderEntities.Customers.Remove(customer); |
|
this.ProviderEntities.SaveChanges(); |
|
} |
|
|
|
/// <summary> |
|
/// Get Password |
|
/// </summary> |
|
/// <param name="user"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string GetPassword(User user, string answer) |
|
{ |
|
if (!this.EnablePasswordRetrieval) |
|
throw new ProviderException(Res.Get<ErrorMessages>().PasswordRetrievalNotEnabled); |
|
|
|
if (user == null) throw new ProviderException(Res.Get<ErrorMessages>().MembershipUserNotFound); |
|
|
|
var passwordFormat = (MembershipPasswordFormat)user.PasswordFormat; |
|
if (passwordFormat == MembershipPasswordFormat.Hashed) |
|
throw new ProviderException(Res.Get<ErrorMessages>().CannotRetrieveHashedPasswords); |
|
|
|
if (user.IsLockedOut) throw new ProviderException(Res.Get<ErrorMessages>().MembershipUserLocked); |
|
|
|
if (this.RequiresQuestionAndAnswer) |
|
{ |
|
if (!string.IsNullOrEmpty(answer)) |
|
{ |
|
var passwordAnswer = user.PasswordAnswer; |
|
if (!this.CheckValidPassword(answer.Trim().ToUpperInvariant(), passwordAnswer, passwordFormat)) |
|
{ |
|
this.UpdateFailureCount(user, "answer"); |
|
throw new ProviderException(Res.Get<ErrorMessages>().WrongPasswordAnswer); |
|
} |
|
} |
|
else |
|
{ |
|
this.UpdateFailureCount(user, "answer"); |
|
throw new ProviderException(Res.Get<ErrorMessages>().PasswordAnswerRequired); |
|
} |
|
} |
|
|
|
var salt = user.Salt; |
|
var str = DecodePassword(user.Password, passwordFormat); |
|
return str.Left(str.Length - salt.Length); |
|
} |
|
|
|
/// <summary> |
|
/// Get Password |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string GetPassword(string userName, string answer) |
|
{ |
|
return this.GetPassword(this.GetUser(userName), answer); |
|
} |
|
|
|
/// <summary> |
|
/// Get Password |
|
/// </summary> |
|
/// <param name="userId"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string GetPassword(Guid userId, string answer) |
|
{ |
|
return this.GetPassword(this.GetUser(userId), answer); |
|
} |
|
|
|
/// <summary> |
|
/// Reset the password |
|
/// </summary> |
|
/// <param name="user"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string ResetPassword(User user, string answer) |
|
{ |
|
if (this.EnablePasswordReset) |
|
{ |
|
if (user != null) |
|
{ |
|
var passwordFormat = (MembershipPasswordFormat)user.PasswordFormat; |
|
if (!user.IsLockedOut) |
|
{ |
|
if (this.RequiresQuestionAndAnswer) |
|
{ |
|
if (!string.IsNullOrEmpty(answer)) |
|
{ |
|
string passwordAnswer = user.PasswordAnswer; |
|
if (!this.CheckValidPassword(answer.Trim().ToUpperInvariant(), passwordAnswer, passwordFormat)) |
|
{ |
|
this.UpdateFailureCount(user, "answer"); |
|
throw new ProviderException(Res.Get<ErrorMessages>().WrongPasswordAnswer); |
|
} |
|
} |
|
else |
|
{ |
|
this.UpdateFailureCount(user, "answer"); |
|
throw new ProviderException(Res.Get<ErrorMessages>().PasswordAnswerRequired); |
|
} |
|
} |
|
|
|
string str = System.Web.Security.Membership.GeneratePassword(this.NewPasswordLength, this.MinRequiredNonAlphanumericCharacters); |
|
string salt = user.Salt; |
|
string str1 = this.EncodePassword(str, salt, passwordFormat); |
|
user.SetLastPasswordChangedDate(DateTime.UtcNow); |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.CustomerId == user.Id); |
|
customer.Password = str1; |
|
|
|
return str; |
|
} |
|
else |
|
{ |
|
throw new ProviderException(Res.Get<ErrorMessages>().MembershipUserLocked); |
|
} |
|
} |
|
else |
|
{ |
|
throw new ProviderException(Res.Get<ErrorMessages>().MembershipUserNotFound); |
|
} |
|
} |
|
else |
|
{ |
|
throw new NotSupportedException(Res.Get<ErrorMessages>().PasswordResetIsNotEnabled); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Reset the password |
|
/// </summary> |
|
/// <param name="userName"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string ResetPassword(string userName, string answer) |
|
{ |
|
return this.ResetPassword(this.GetUser(userName), answer); |
|
} |
|
|
|
/// <summary> |
|
/// Reset the password |
|
/// </summary> |
|
/// <param name="userId"></param> |
|
/// <param name="answer"></param> |
|
/// <returns></returns> |
|
public override string ResetPassword(Guid userId, string answer) |
|
{ |
|
return this.ResetPassword(this.GetUser(userId), answer); |
|
} |
|
#endregion |
|
|
|
#region Not Supported |
|
|
|
public override bool ChangePassword(User user, string oldPassword, string newPassword) |
|
{ |
|
base.ChangePassword(user, oldPassword, newPassword); |
|
MembershipPasswordFormat passwordFormat = (MembershipPasswordFormat)user.PasswordFormat; |
|
var customer = this.ProviderEntities.Customers.SingleOrDefault(c => c.CustomerId == user.Id); |
|
customer.Password = this.EncodePassword(newPassword, user.Salt, passwordFormat); |
|
|
|
return true; |
|
} |
|
#endregion |
|
} |
|
} |