|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Web;
|
|
|
|
|
using System.Web.Mvc;
|
|
|
|
|
using System.Web.Security;
|
|
|
|
|
|
|
|
|
|
namespace NzbDrone.Web.Models
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
#region Models
|
|
|
|
|
[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
|
|
|
|
public class ChangePasswordModel
|
|
|
|
|
{
|
|
|
|
|
[Required]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("Current password")]
|
|
|
|
|
public string OldPassword { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[ValidatePasswordLength]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("New password")]
|
|
|
|
|
public string NewPassword { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("Confirm new password")]
|
|
|
|
|
public string ConfirmPassword { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class LogOnModel
|
|
|
|
|
{
|
|
|
|
|
[Required]
|
|
|
|
|
[DisplayName("User name")]
|
|
|
|
|
public string UserName { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("Password")]
|
|
|
|
|
public string Password { get; set; }
|
|
|
|
|
|
|
|
|
|
[DisplayName("Remember me?")]
|
|
|
|
|
public bool RememberMe { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirmation password do not match.")]
|
|
|
|
|
public class RegisterModel
|
|
|
|
|
{
|
|
|
|
|
[Required]
|
|
|
|
|
[DisplayName("User name")]
|
|
|
|
|
public string UserName { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[DataType(DataType.EmailAddress)]
|
|
|
|
|
[DisplayName("Email address")]
|
|
|
|
|
public string Email { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[ValidatePasswordLength]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("Password")]
|
|
|
|
|
public string Password { get; set; }
|
|
|
|
|
|
|
|
|
|
[Required]
|
|
|
|
|
[DataType(DataType.Password)]
|
|
|
|
|
[DisplayName("Confirm password")]
|
|
|
|
|
public string ConfirmPassword { get; set; }
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Services
|
|
|
|
|
// The FormsAuthentication type is sealed and contains static members, so it is difficult to
|
|
|
|
|
// unit test code that calls its members. The interface and helper class below demonstrate
|
|
|
|
|
// how to create an abstract wrapper around such a type in order to make the AccountController
|
|
|
|
|
// code unit testable.
|
|
|
|
|
|
|
|
|
|
public interface IMembershipService
|
|
|
|
|
{
|
|
|
|
|
int MinPasswordLength { get; }
|
|
|
|
|
|
|
|
|
|
bool ValidateUser(string userName, string password);
|
|
|
|
|
MembershipCreateStatus CreateUser(string userName, string password, string email);
|
|
|
|
|
bool ChangePassword(string userName, string oldPassword, string newPassword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class AccountMembershipService : IMembershipService
|
|
|
|
|
{
|
|
|
|
|
private readonly MembershipProvider _provider;
|
|
|
|
|
|
|
|
|
|
public AccountMembershipService()
|
|
|
|
|
: this(null)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public AccountMembershipService(MembershipProvider provider)
|
|
|
|
|
{
|
|
|
|
|
_provider = provider ?? Membership.Provider;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int MinPasswordLength
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _provider.MinRequiredPasswordLength;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ValidateUser(string userName, string password)
|
|
|
|
|
{
|
|
|
|
|
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
|
|
|
|
|
if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
|
|
|
|
|
|
|
|
|
|
return _provider.ValidateUser(userName, password);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MembershipCreateStatus CreateUser(string userName, string password, string email)
|
|
|
|
|
{
|
|
|
|
|
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
|
|
|
|
|
if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
|
|
|
|
|
if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
|
|
|
|
|
|
|
|
|
|
MembershipCreateStatus status;
|
|
|
|
|
_provider.CreateUser(userName, password, email, null, null, true, null, out status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ChangePassword(string userName, string oldPassword, string newPassword)
|
|
|
|
|
{
|
|
|
|
|
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
|
|
|
|
|
if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
|
|
|
|
|
if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");
|
|
|
|
|
|
|
|
|
|
// The underlying ChangePassword() will throw an exception rather
|
|
|
|
|
// than return false in certain failure scenarios.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
|
|
|
|
|
return currentUser.ChangePassword(oldPassword, newPassword);
|
|
|
|
|
}
|
|
|
|
|
catch (ArgumentException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
catch (MembershipPasswordException)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IFormsAuthenticationService
|
|
|
|
|
{
|
|
|
|
|
void SignIn(string userName, bool createPersistentCookie);
|
|
|
|
|
void SignOut();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class FormsAuthenticationService : IFormsAuthenticationService
|
|
|
|
|
{
|
|
|
|
|
public void SignIn(string userName, bool createPersistentCookie)
|
|
|
|
|
{
|
|
|
|
|
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
|
|
|
|
|
|
|
|
|
|
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SignOut()
|
|
|
|
|
{
|
|
|
|
|
FormsAuthentication.SignOut();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Validation
|
|
|
|
|
public static class AccountValidation
|
|
|
|
|
{
|
|
|
|
|
public static string ErrorCodeToString(MembershipCreateStatus createStatus)
|
|
|
|
|
{
|
|
|
|
|
// See http://go.microsoft.com/fwlink/?LinkID=177550 for
|
|
|
|
|
// a full list of status codes.
|
|
|
|
|
switch (createStatus)
|
|
|
|
|
{
|
|
|
|
|
case MembershipCreateStatus.DuplicateUserName:
|
|
|
|
|
return "Username already exists. Please enter a different user name.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.DuplicateEmail:
|
|
|
|
|
return "A username for that e-mail address already exists. Please enter a different e-mail address.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.InvalidPassword:
|
|
|
|
|
return "The password provided is invalid. Please enter a valid password value.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.InvalidEmail:
|
|
|
|
|
return "The e-mail address provided is invalid. Please check the value and try again.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.InvalidAnswer:
|
|
|
|
|
return "The password retrieval answer provided is invalid. Please check the value and try again.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.InvalidQuestion:
|
|
|
|
|
return "The password retrieval question provided is invalid. Please check the value and try again.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.InvalidUserName:
|
|
|
|
|
return "The user name provided is invalid. Please check the value and try again.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.ProviderError:
|
|
|
|
|
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
|
|
|
|
|
|
|
|
|
|
case MembershipCreateStatus.UserRejected:
|
|
|
|
|
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
|
|
|
|
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
|
|
|
|
|
{
|
|
|
|
|
private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
|
|
|
|
|
private readonly object _typeId = new object();
|
|
|
|
|
|
|
|
|
|
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
|
|
|
|
|
: base(_defaultErrorMessage)
|
|
|
|
|
{
|
|
|
|
|
OriginalProperty = originalProperty;
|
|
|
|
|
ConfirmProperty = confirmProperty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string ConfirmProperty { get; private set; }
|
|
|
|
|
public string OriginalProperty { get; private set; }
|
|
|
|
|
|
|
|
|
|
public override object TypeId
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _typeId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string FormatErrorMessage(string name)
|
|
|
|
|
{
|
|
|
|
|
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
|
|
|
|
|
OriginalProperty, ConfirmProperty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool IsValid(object value)
|
|
|
|
|
{
|
|
|
|
|
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
|
|
|
|
|
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
|
|
|
|
|
object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
|
|
|
|
|
return Object.Equals(originalValue, confirmValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
|
|
|
|
public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
|
|
|
|
|
{
|
|
|
|
|
private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
|
|
|
|
|
private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;
|
|
|
|
|
|
|
|
|
|
public ValidatePasswordLengthAttribute()
|
|
|
|
|
: base(_defaultErrorMessage)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string FormatErrorMessage(string name)
|
|
|
|
|
{
|
|
|
|
|
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
|
|
|
|
|
name, _minCharacters);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool IsValid(object value)
|
|
|
|
|
{
|
|
|
|
|
string valueAsString = value as string;
|
|
|
|
|
return (valueAsString != null && valueAsString.Length >= _minCharacters);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
}
|