using System; using System.Data.SqlTypes; using System.Linq; using System.Security.Cryptography; using System.Web; using System.Web.Security; using MileageTraker.Web.Context; using MileageTraker.Web.Models; namespace MileageTraker.Web.Membership { public class CodeFirstMembershipProvider : MembershipProvider { #region Properties private const int TokenSizeInBytes = 16; public override string ApplicationName { get { return GetType().Assembly.GetName().Name; } set { if (value == null) throw new ArgumentNullException("value"); ApplicationName = GetType().Assembly.GetName().Name; } } public override int MaxInvalidPasswordAttempts { get { return 5; } } public override int MinRequiredNonAlphanumericCharacters { get { return 0; } } public override int MinRequiredPasswordLength { get { return 6; } } public override int PasswordAttemptWindow { get { return 0; } } public override MembershipPasswordFormat PasswordFormat { get { return MembershipPasswordFormat.Hashed; } } public override string PasswordStrengthRegularExpression { get { return String.Empty; } } public override bool RequiresUniqueEmail { get { return true; } } #endregion #region Functions public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { if (string.IsNullOrEmpty(username)) { status = MembershipCreateStatus.InvalidUserName; return null; } if (string.IsNullOrEmpty(password)) { status = MembershipCreateStatus.InvalidPassword; return null; } if (string.IsNullOrEmpty(email)) { status = MembershipCreateStatus.InvalidEmail; return null; } var hashedPassword = Crypto.HashPassword(password); if (hashedPassword.Length > 128) { status = MembershipCreateStatus.InvalidPassword; return null; } using (var context = new MileageTrakerContext()) { if (context.Users.Any(usr => usr.Username == username)) { status = MembershipCreateStatus.DuplicateUserName; return null; } if (context.Users.Any(usr => usr.Email == email)) { status = MembershipCreateStatus.DuplicateEmail; return null; } var newUser = new User { UserId = Guid.NewGuid(), Username = username, Password = hashedPassword, IsApproved = isApproved, Email = email, CreateDate = DateTime.UtcNow, LastPasswordChangedDate = DateTime.UtcNow, PasswordFailuresSinceLastSuccess = 0, LastLoginDate = DateTime.UtcNow, LastActivityDate = DateTime.UtcNow, LastLockoutDate = SqlDateTime.MinValue.Value, IsLockedOut = false, LastPasswordFailureDate = SqlDateTime.MinValue.Value }; context.Users.Add(newUser); context.SaveChanges(); status = MembershipCreateStatus.Success; return newUser.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); } } public string CreateUserAndAccount(string userName, string password, bool requireConfirmation) { return CreateAccount(userName, password, requireConfirmation); } public override bool ValidateUser(string username, string password) { if (string.IsNullOrEmpty(username)) { return false; } if (string.IsNullOrEmpty(password)) { return false; } using (var context = new MileageTrakerContext()) { User user = context.Users.FirstOrDefault(usr => usr.Username == username); if (user == null) { return false; } if (!user.IsApproved) { return false; } if (user.IsLockedOut) { return false; } var hashedPassword = user.Password; var verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, password)); if (verificationSucceeded) { user.PasswordFailuresSinceLastSuccess = 0; user.LastLoginDate = DateTime.UtcNow; user.LastActivityDate = DateTime.UtcNow; } else { var failures = user.PasswordFailuresSinceLastSuccess; if (failures < MaxInvalidPasswordAttempts) { user.PasswordFailuresSinceLastSuccess += 1; user.LastPasswordFailureDate = DateTime.UtcNow; } else if (failures >= MaxInvalidPasswordAttempts) { user.LastPasswordFailureDate = DateTime.UtcNow; user.LastLockoutDate = DateTime.UtcNow; user.IsLockedOut = true; } } context.SaveChanges(); return verificationSucceeded; } } public override MembershipUser GetUser(string username, bool userIsOnline) { if (string.IsNullOrEmpty(username)) { return null; } using (var context = new MileageTrakerContext()) { var user = context.Users.FirstOrDefault(usr => usr.Username == username); if (user == null) { return null; } if (userIsOnline) { user.LastActivityDate = DateTime.UtcNow; context.SaveChanges(); } return user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); } } public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) { if (providerUserKey is Guid) { } else { return null; } using (var context = new MileageTrakerContext()) { var user = context.Users.Find(providerUserKey); if (user == null) { return null; } if (userIsOnline) { user.LastActivityDate = DateTime.UtcNow; context.SaveChanges(); } return user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); } } public override bool ChangePassword(string username, string oldPassword, string newPassword) { if (string.IsNullOrEmpty(username)) { return false; } if (string.IsNullOrEmpty(oldPassword)) { return false; } if (string.IsNullOrEmpty(newPassword)) { return false; } using (var context = new MileageTrakerContext()) { User user = context.Users.FirstOrDefault(usr => usr.Username == username); if (user == null) { return false; } var hashedPassword = user.Password; var verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, oldPassword)); if (verificationSucceeded) { user.PasswordFailuresSinceLastSuccess = 0; } else { var failures = user.PasswordFailuresSinceLastSuccess; if (failures < MaxInvalidPasswordAttempts) { user.PasswordFailuresSinceLastSuccess += 1; user.LastPasswordFailureDate = DateTime.UtcNow; } else if (failures >= MaxInvalidPasswordAttempts) { user.LastPasswordFailureDate = DateTime.UtcNow; user.LastLockoutDate = DateTime.UtcNow; user.IsLockedOut = true; } context.SaveChanges(); return false; } var newHashedPassword = Crypto.HashPassword(newPassword); if (newHashedPassword.Length > 128) { return false; } user.Password = newHashedPassword; user.LastPasswordChangedDate = DateTime.UtcNow; context.SaveChanges(); return true; } } public override bool UnlockUser(string userName) { using (var context = new MileageTrakerContext()) { var user = context.Users.FirstOrDefault(usr => usr.Username == userName); if (user != null) { user.IsLockedOut = false; user.PasswordFailuresSinceLastSuccess = 0; context.SaveChanges(); return true; } return false; } } public override int GetNumberOfUsersOnline() { var dateActive = DateTime.UtcNow.Subtract( TimeSpan.FromMinutes( Convert.ToDouble( System.Web.Security.Membership.UserIsOnlineTimeWindow))); using (var context = new MileageTrakerContext()) { return context.Users.Count(usr => usr.LastActivityDate > dateActive); } } public override bool DeleteUser(string username, bool deleteAllRelatedData) { if (string.IsNullOrEmpty(username)) { return false; } using (var context = new MileageTrakerContext()) { var user = context.Users.FirstOrDefault(usr => usr.Username == username); if (user != null) { context.Users.Remove(user); context.SaveChanges(); return true; } return false; } } public override string GetUserNameByEmail(string email) { using (var context = new MileageTrakerContext()) { var user = context.Users.FirstOrDefault(usr => usr.Email == email); if (user != null) { return user.Username; } return string.Empty; } } public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { var membershipUsers = new MembershipUserCollection(); using (var context = new MileageTrakerContext()) { totalRecords = context.Users.Count(Usr => Usr.Email == emailToMatch); var users = context.Users.Where(usr => usr.Email == emailToMatch) .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { membershipUsers.Add( user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); } } return membershipUsers; } public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { var membershipUsers = new MembershipUserCollection(); using (var context = new MileageTrakerContext()) { totalRecords = context.Users.Count(Usr => Usr.Username == usernameToMatch); var users = context.Users.Where(usr => usr.Username == usernameToMatch) .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { membershipUsers.Add(user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); } } return membershipUsers; } public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { var membershipUsers = new MembershipUserCollection(); using (var context = new MileageTrakerContext()) { totalRecords = context.Users.Count(); var users = context.Users .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { membershipUsers.Add(user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); } } return membershipUsers; } public string CreateAccount(string userName, string password, bool requireConfirmationToken) { if (string.IsNullOrEmpty(userName)) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); } if (string.IsNullOrEmpty(password)) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } var hashedPassword = Crypto.HashPassword(password); if (hashedPassword.Length > 128) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } using (var context = new MileageTrakerContext()) { if (context.Users.Any(usr => usr.Username == userName)) { throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); } var token = string.Empty; if (requireConfirmationToken) { token = GenerateToken(); } var newUser = new User { UserId = Guid.NewGuid(), Username = userName, Password = hashedPassword, IsApproved = !requireConfirmationToken, Email = string.Empty, CreateDate = DateTime.UtcNow, LastPasswordChangedDate = DateTime.UtcNow, PasswordFailuresSinceLastSuccess = 0, LastLoginDate = DateTime.UtcNow, LastActivityDate = DateTime.UtcNow, LastLockoutDate = SqlDateTime.MinValue.Value, IsLockedOut = false, LastPasswordFailureDate = SqlDateTime.MinValue.Value, ConfirmationToken = token }; context.Users.Add(newUser); context.SaveChanges(); return token; } } private static string GenerateToken() { using (var prng = new RNGCryptoServiceProvider()) { return GenerateToken(prng); } } private static string GenerateToken(RandomNumberGenerator generator) { var tokenBytes = new byte[TokenSizeInBytes]; generator.GetBytes(tokenBytes); return HttpServerUtility.UrlTokenEncode(tokenBytes); } #endregion #region Not Supported //CodeFirstMembershipProvider does not support password retrieval scenarios. public override bool EnablePasswordRetrieval { get { return false; } } //CodeFirstMembershipProvider does not support password reset scenarios. public override bool EnablePasswordReset { get { return false; } } //CodeFirstMembershipProvider does not support question and answer scenarios. public override bool RequiresQuestionAndAnswer { get { return false; } } public override string GetPassword(string username, string answer) { throw new NotSupportedException("Consider using methods from WebSecurity module."); } public override string ResetPassword(string username, string answer) { throw new NotSupportedException("Consider using methods from WebSecurity module."); } public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { throw new NotSupportedException("Consider using methods from WebSecurity module."); } //CodeFirstMembershipProvider does not support UpdateUser because this method is useless. public override void UpdateUser(MembershipUser user) { throw new NotSupportedException(); } #endregion } }