PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/MM.Web/Models/AccountModels.cs

#
C# | 261 lines | 204 code | 49 blank | 8 comment | 12 complexity | 70ed468bd6694622f4429098afe64991 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.ComponentModel.DataAnnotations;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Web;
  8. using System.Web.Mvc;
  9. using System.Web.Security;
  10. namespace MM.Web.Models
  11. {
  12. #region Models
  13. [PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
  14. public class ChangePasswordModel
  15. {
  16. [Required]
  17. [DataType(DataType.Password)]
  18. [DisplayName("Current password")]
  19. public string OldPassword { get; set; }
  20. [Required]
  21. [ValidatePasswordLength]
  22. [DataType(DataType.Password)]
  23. [DisplayName("New password")]
  24. public string NewPassword { get; set; }
  25. [Required]
  26. [DataType(DataType.Password)]
  27. [DisplayName("Confirm new password")]
  28. public string ConfirmPassword { get; set; }
  29. }
  30. public class LogOnModel
  31. {
  32. [Required]
  33. [DisplayName("User name")]
  34. public string UserName { get; set; }
  35. [Required]
  36. [DataType(DataType.Password)]
  37. [DisplayName("Password")]
  38. public string Password { get; set; }
  39. [DisplayName("Remember me?")]
  40. public bool RememberMe { get; set; }
  41. }
  42. [PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirmation password do not match.")]
  43. public class RegisterModel
  44. {
  45. [Required]
  46. [DisplayName("User name")]
  47. public string UserName { get; set; }
  48. [Required]
  49. [DataType(DataType.EmailAddress)]
  50. [DisplayName("Email address")]
  51. public string Email { get; set; }
  52. [Required]
  53. [ValidatePasswordLength]
  54. [DataType(DataType.Password)]
  55. [DisplayName("Password")]
  56. public string Password { get; set; }
  57. [Required]
  58. [DataType(DataType.Password)]
  59. [DisplayName("Confirm password")]
  60. public string ConfirmPassword { get; set; }
  61. }
  62. #endregion
  63. #region Services
  64. // The FormsAuthentication type is sealed and contains static members, so it is difficult to
  65. // unit test code that calls its members. The interface and helper class below demonstrate
  66. // how to create an abstract wrapper around such a type in order to make the AccountController
  67. // code unit testable.
  68. public interface IMembershipService
  69. {
  70. int MinPasswordLength { get; }
  71. bool ValidateUser(string userName, string password);
  72. MembershipCreateStatus CreateUser(string userName, string password, string email);
  73. bool ChangePassword(string userName, string oldPassword, string newPassword);
  74. }
  75. public class AccountMembershipService : IMembershipService
  76. {
  77. private readonly MembershipProvider _provider;
  78. public AccountMembershipService()
  79. : this(null) {
  80. }
  81. public AccountMembershipService(MembershipProvider provider) {
  82. _provider = provider ?? Membership.Provider;
  83. }
  84. public int MinPasswordLength {
  85. get {
  86. return _provider.MinRequiredPasswordLength;
  87. }
  88. }
  89. public bool ValidateUser(string userName, string password) {
  90. if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
  91. if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
  92. return _provider.ValidateUser(userName, password);
  93. }
  94. public MembershipCreateStatus CreateUser(string userName, string password, string email) {
  95. if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
  96. if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
  97. if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
  98. MembershipCreateStatus status;
  99. _provider.CreateUser(userName, password, email, null, null, true, null, out status);
  100. return status;
  101. }
  102. public bool ChangePassword(string userName, string oldPassword, string newPassword) {
  103. if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
  104. if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
  105. if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");
  106. // The underlying ChangePassword() will throw an exception rather
  107. // than return false in certain failure scenarios.
  108. try {
  109. MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
  110. return currentUser.ChangePassword(oldPassword, newPassword);
  111. }
  112. catch (ArgumentException) {
  113. return false;
  114. }
  115. catch (MembershipPasswordException) {
  116. return false;
  117. }
  118. }
  119. }
  120. public interface IFormsAuthenticationService
  121. {
  122. void SignIn(string userName, bool createPersistentCookie);
  123. void SignOut();
  124. }
  125. public class FormsAuthenticationService : IFormsAuthenticationService
  126. {
  127. public void SignIn(string userName, bool createPersistentCookie) {
  128. if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
  129. FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
  130. }
  131. public void SignOut() {
  132. FormsAuthentication.SignOut();
  133. }
  134. }
  135. #endregion
  136. #region Validation
  137. public static class AccountValidation
  138. {
  139. public static string ErrorCodeToString(MembershipCreateStatus createStatus) {
  140. // See http://go.microsoft.com/fwlink/?LinkID=177550 for
  141. // a full list of status codes.
  142. switch (createStatus) {
  143. case MembershipCreateStatus.DuplicateUserName:
  144. return "Username already exists. Please enter a different user name.";
  145. case MembershipCreateStatus.DuplicateEmail:
  146. return "A username for that e-mail address already exists. Please enter a different e-mail address.";
  147. case MembershipCreateStatus.InvalidPassword:
  148. return "The password provided is invalid. Please enter a valid password value.";
  149. case MembershipCreateStatus.InvalidEmail:
  150. return "The e-mail address provided is invalid. Please check the value and try again.";
  151. case MembershipCreateStatus.InvalidAnswer:
  152. return "The password retrieval answer provided is invalid. Please check the value and try again.";
  153. case MembershipCreateStatus.InvalidQuestion:
  154. return "The password retrieval question provided is invalid. Please check the value and try again.";
  155. case MembershipCreateStatus.InvalidUserName:
  156. return "The user name provided is invalid. Please check the value and try again.";
  157. case MembershipCreateStatus.ProviderError:
  158. return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
  159. case MembershipCreateStatus.UserRejected:
  160. return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
  161. default:
  162. return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
  163. }
  164. }
  165. }
  166. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
  167. public sealed class PropertiesMustMatchAttribute : ValidationAttribute
  168. {
  169. private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
  170. private readonly object _typeId = new object();
  171. public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
  172. : base(_defaultErrorMessage) {
  173. OriginalProperty = originalProperty;
  174. ConfirmProperty = confirmProperty;
  175. }
  176. public string ConfirmProperty { get; private set; }
  177. public string OriginalProperty { get; private set; }
  178. public override object TypeId {
  179. get {
  180. return _typeId;
  181. }
  182. }
  183. public override string FormatErrorMessage(string name) {
  184. return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
  185. OriginalProperty, ConfirmProperty);
  186. }
  187. public override bool IsValid(object value) {
  188. PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
  189. object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
  190. object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
  191. return Object.Equals(originalValue, confirmValue);
  192. }
  193. }
  194. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
  195. public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
  196. {
  197. private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
  198. private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;
  199. public ValidatePasswordLengthAttribute()
  200. : base(_defaultErrorMessage) {
  201. }
  202. public override string FormatErrorMessage(string name) {
  203. return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
  204. name, _minCharacters);
  205. }
  206. public override bool IsValid(object value) {
  207. string valueAsString = value as string;
  208. return (valueAsString != null && valueAsString.Length >= _minCharacters);
  209. }
  210. }
  211. #endregion
  212. }