using System ;
using System.ComponentModel ;
using System.ComponentModel.DataAnnotations ;
using System.Globalization ;
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 ;
}
#region IMembershipService Members
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 ;
}
}
# endregion
}
public interface IFormsAuthenticationService
{
void SignIn ( string userName , bool createPersistentCookie ) ;
void SignOut ( ) ;
}
public class FormsAuthenticationService : IFormsAuthenticationService
{
#region IFormsAuthenticationService Members
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
}
# 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 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
}