using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Security;
using Persistence;
namespace Service.Provider {
class HeuristicLabMembershipProvider : MembershipProvider {
#region variables
private string pApplicationName;
private bool pEnablePasswordReset;
private bool pEnablePasswordRetrieval;
private bool pRequiresQuestionAndAnswer;
private bool pRequiresUniqueEmail;
private int pMaxInvalidPasswordAttempts = 3;
private int pPasswordAttemptWindow;
private int pMinRequiredPasswordLength = 5;
private MembershipPasswordFormat pPasswordFormat = MembershipPasswordFormat.Clear;
#endregion
#region properties
public override string ApplicationName {
get { return pApplicationName; }
set { pApplicationName = value; }
}
public override bool EnablePasswordReset {
get { return pEnablePasswordReset; }
}
public override bool EnablePasswordRetrieval {
get { return pEnablePasswordRetrieval; }
}
public override int MaxInvalidPasswordAttempts {
get { return pMaxInvalidPasswordAttempts; }
}
public override int MinRequiredNonAlphanumericCharacters {
get { return 0; }
}
public override int MinRequiredPasswordLength {
get { return pMinRequiredPasswordLength; }
}
public override int PasswordAttemptWindow {
get { return pPasswordAttemptWindow; }
}
public override MembershipPasswordFormat PasswordFormat {
get { return pPasswordFormat; }
}
public override string PasswordStrengthRegularExpression {
get { return string.Empty; }
}
public override bool RequiresQuestionAndAnswer {
get { return false; }
}
public override bool RequiresUniqueEmail {
get { return pRequiresUniqueEmail; }
}
#endregion
///
/// this is the methode to initialize all important and needed
/// variables and settings
/// The most settings will be read from the web.config
///
///
///
///
public override void Initialize(string name, NameValueCollection config) {
//
// Initialize values from web.config.
//
if (config == null)
throw new ArgumentNullException("config");
if (string.IsNullOrEmpty(name) || name.Length == 0)
name = "HeuristicLabMembershipProvider";
if (String.IsNullOrEmpty(config["description"])) {
config.Remove("description");
config.Add("description", "Heuristic Lab Membership provider");
}
// Initialize the abstract base class.
base.Initialize(name, config);
pApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
string tempFormat = config["passwordFormat"];
if (tempFormat == null) {
tempFormat = "Clear";
}
switch (tempFormat) {
case "Hashed":
pPasswordFormat = MembershipPasswordFormat.Hashed;
break;
case "Encrypted":
pPasswordFormat = MembershipPasswordFormat.Encrypted;
break;
case "Clear":
pPasswordFormat = MembershipPasswordFormat.Clear;
break;
default:
throw new ProviderException("Password format not supported.");
}
}
///
/// this mehtode change the password of an existent user
/// the methode look for db connection and connect to
/// such db if it possible
/// Also neccessary requirements of password complexity is used
/// by this methode
///
///
///
///
///
/// return true if password is changed, or false if it is not able
/// to set the password
///
public override bool ChangePassword(string username, string oldPassword, string newPassword) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// check database connection
if (db == null) {
return false;
}
if (newPassword.Length < MinRequiredPasswordLength) {
return false;
}
try {
// try to get user
HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
if (u.Password == EncodePassword(oldPassword) && newPassword.Length > 0) {
u.Password = EncodePassword(newPassword);
db.SubmitChanges();
return true;
}
return false;
}
catch (Exception) {
return false;
}
}
}
///
/// change the password questionandanswer for specified user
///
///
///
///
///
///
///
/// return true if changes are submitted
/// false if are no user found
///
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0 && newPasswordQuestion.Length > 0 && newPasswordAnswer.Length > 0) {
HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username);
u.PasswordAnswer = newPasswordAnswer;
u.PasswordQuestion = newPasswordQuestion;
db.SubmitChanges();
return true;
}
return false;
}
}
///
/// create a new user with username, password
/// email, passwordquestion, passwordAnswer,
/// Approvedstate, whether is providerUserKey and certain creation MembershipCreateStatus status
///
///
///
///
///
///
///
///
///
///
/// return a created user if there is no error occoured
/// return null if there is an error occoured
///
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// check database connection
if (db == null) {
status = MembershipCreateStatus.ProviderError;
return null;
}
try {
// check for duplicate entries
if (db.HeuristicLabUsers.Count(x => x.UserName == username) > 0) {
status = MembershipCreateStatus.DuplicateUserName;
return null;
}
if (db.HeuristicLabUsers.Count(x => x.Email == email) > 0) {
status = MembershipCreateStatus.DuplicateEmail;
return null;
}
if (password.Length < MinRequiredPasswordLength) {
status = MembershipCreateStatus.InvalidPassword;
return null;
}
// create new user
HeuristicLabUser u = new HeuristicLabUser(username, email, passwordQuestion, "");
password = EncodePassword(password);
u.Password = password;
u.PasswordAnswer = passwordAnswer;
u.PasswordQuestion = passwordQuestion;
// save user into database
db.HeuristicLabUsers.InsertOnSubmit(u);
db.SubmitChanges();
// success
status = MembershipCreateStatus.Success;
return u.getMembershipUser(this.Name);
}
catch (Exception) {
// error
status = MembershipCreateStatus.ProviderError;
return null;
}
}
}
public override bool DeleteUser(string username, bool deleteAllRelatedData) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// check database connection
if (db == null) {
return false;
}
try {
// try to get user
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.UserName == username);
// optionally delete related data
if (deleteAllRelatedData) {
db.HeuristicLabUserRole.DeleteAllOnSubmit(u.HeuristicLabUserRoles);
}
// delete user
db.HeuristicLabUsers.DeleteOnSubmit(u);
db.SubmitChanges();
return true;
}
catch (Exception) {
return false;
}
}
}
///
/// Returns a paged collection of users that match a given e-mail address
/// TODO: Return a sorted result set
///
///
///
///
///
///
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
IQueryable users = (from u in db.HeuristicLabUsers where u.Email == emailToMatch select u);
return PagedCollection(users, pageIndex, pageSize, out totalRecords);
}
}
///
/// Returns a paged collection of users that match a given username
/// TODO: Return a sorted result set
///
///
///
///
///
///
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
IQueryable users = (from u in db.HeuristicLabUsers where u.UserName == usernameToMatch select u);
return PagedCollection(users, pageIndex, pageSize, out totalRecords);
}
}
///
/// Returns a paged collection of all users
/// TODO: Return a sorted result set
///
///
///
///
///
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// orderby u.UserName
// skipped ordering for now as the default comparator seems to be wrong
IQueryable users = (from u in db.HeuristicLabUsers select u);
return PagedCollection(users, pageIndex, pageSize, out totalRecords);
}
}
///
/// Helper method that takes an IQueriable object and returns a paged part of it
///
///
///
///
///
///
private MembershipUserCollection PagedCollection(IQueryable querySource, int pageIndex, int pageSize, out int totalRecords) {
totalRecords = querySource.Count();
MembershipUserCollection userCollection = new MembershipUserCollection();
int skip = (pageIndex == 0) ? 0 : (pageIndex * pageSize);
foreach (HeuristicLabUser u in querySource.Skip(skip).Take(pageSize)) {
userCollection.Add(u.getMembershipUser(this.Name));
}
return userCollection;
}
///
/// not jet implemented returns 0 as default
///
///
public override int GetNumberOfUsersOnline() {
return 0;
}
public override string GetPassword(string username, string answer) {
if (PasswordFormat == MembershipPasswordFormat.Hashed)
throw new NotSupportedException("Passwort is hashed");
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.UserName == username);
if (EnablePasswordRetrieval && RequiresQuestionAndAnswer) {
if (u.PasswordAnswer.Equals(answer)) {
return u.Password;
} else {
throw new MembershipPasswordException();
}
} else throw new NotSupportedException("PasswortRetrival or RequiresQuestionAndAnswer not set");
}
}
public override MembershipUser GetUser(string username, bool userIsOnline) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.UserName == username);
if (u != null)
return u.getMembershipUser(this.Name);
else
return null;
}
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) {
long ID = providerUserKey is long ? (long)providerUserKey : -1;
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.ID == ID);
if (u != null)
return u.getMembershipUser(this.Name);
else
return null;
}
}
public override string GetUserNameByEmail(string email) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.Email == email);
if (u != null)
return u.UserName;
else
return null;
}
}
public override string ResetPassword(string username, string answer) {
throw new NotSupportedException("Restet password not avaliable");
}
public bool LockUser(string userName) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// check database connection
if (db == null) {
return false;
}
try {
// try to get user
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.UserName == userName);
// unlock user
u.Locked = true;
db.SubmitChanges();
return true;
}
catch (Exception) {
return false;
}
}
}
public override bool UnlockUser(string userName) {
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
// check database connection
if (db == null) {
return false;
}
try {
// try to get user
HeuristicLabUser u =
db.HeuristicLabUsers.Single(x => x.UserName == userName);
// unlock user
u.Locked = false;
db.SubmitChanges();
return true;
}
catch (Exception) {
return false;
}
}
}
public override void UpdateUser(MembershipUser user) {
throw new NotImplementedException();
}
///
/// Validates a user
///
///
///
///
public override bool ValidateUser(string username, string password) {
bool isValid = false;
using (DataClassesDataContext db = DatabaseUtil.createDataClassesDataContext()) {
if (db == null) {
return false;
}
if (db.HeuristicLabUsers.Count(x => x.UserName == username && x.Locked == false) > 0) {
HeuristicLabUser u = db.HeuristicLabUsers.Single(x => x.UserName == username && x.Locked == false);
isValid = CheckPassword(password, u.Password) && u.FailedLogins <= MaxInvalidPasswordAttempts;
if (!isValid) {
u.FailedLogins++;
if (u.FailedLogins > MaxInvalidPasswordAttempts) {
u.Locked = true;
}
} else {
u.FailedLogins = 0;
}
db.SubmitChanges();
}
}
return isValid;
}
///
/// compaiers to passwords
///
///
///
///
private bool CheckPassword(string password, string dbpassword) {
string pass1 = password;
string pass2 = dbpassword;
switch (PasswordFormat) {
case MembershipPasswordFormat.Encrypted:
pass2 = DecodePassword(dbpassword);
break;
case MembershipPasswordFormat.Hashed:
pass1 = EncodePassword(password);
break;
default:
break;
}
if (pass1 == pass2) {
return true;
}
return false;
}
///
/// Encodes a password.
/// Public because needed for unit testing.
///
///
///
public string EncodePassword(string password) {
string encodedPassword = password;
switch (PasswordFormat) {
case MembershipPasswordFormat.Clear:
break;
case MembershipPasswordFormat.Encrypted:
encodedPassword =
Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
break;
case MembershipPasswordFormat.Hashed:
SHA512 sha512 = SHA512.Create();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] combined = encoder.GetBytes(password);
sha512.ComputeHash(combined);
encodedPassword = Convert.ToBase64String(sha512.Hash);
break;
default:
throw new ProviderException("Unsupported password format.");
}
return encodedPassword;
}
private readonly byte[] _rgbKey = new byte[]
{
182, 140, 37, 101, 52, 157, 80, 17, 65, 35, 130, 208, 101, 68, 161, 45, 197, 102, 112, 190,
187, 177, 37, 76, 63, 38, 190, 117, 247, 122, 94, 17
};
private readonly byte[] _rgbIv = new byte[] { 60, 121, 178, 142, 50, 160, 226, 84, 41, 66, 158, 180, 26, 232, 42, 113 };
protected override byte[] EncryptPassword(byte[] password) {
SymmetricAlgorithm sa = Aes.Create();
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, sa.CreateEncryptor(_rgbKey, _rgbIv), CryptoStreamMode.Write);
csEncrypt.Write(password, 0, password.Length);
csEncrypt.Close();
byte[] encryptedTextBytes = msEncrypt.ToArray();
msEncrypt.Close();
return encryptedTextBytes;
}
protected override byte[] DecryptPassword(byte[] encodedPassword) {
SymmetricAlgorithm sa = Aes.Create();
MemoryStream msDecrypt = new MemoryStream(encodedPassword);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(_rgbKey, _rgbIv), CryptoStreamMode.Read);
byte[] decryptedTextBytes = new Byte[encodedPassword.Length];
csDecrypt.Read(decryptedTextBytes, 0, encodedPassword.Length);
csDecrypt.Close();
msDecrypt.Close();
return decryptedTextBytes;
}
///
/// Decodes a encoded Password
///
///
///
private string DecodePassword(string encodedPassword) {
string password = encodedPassword;
switch (PasswordFormat) {
case MembershipPasswordFormat.Clear:
break;
case MembershipPasswordFormat.Encrypted:
password =
Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password))).TrimEnd('\0');
break;
case MembershipPasswordFormat.Hashed:
throw new ProviderException("Cannot unencode a hashed password.");
default:
throw new ProviderException("Unsupported password format.");
}
return password;
}
///
/// returns the configuration string, if the value is null or empty the default value is returned
///
///
///
///
private string GetConfigValue(string configValue, string defaultValue) {
if (String.IsNullOrEmpty(configValue))
return defaultValue;
return configValue;
}
}
}