2012-11-05 14:42:21 -01:00
using System ;
using System.Collections.Generic ;
2014-01-06 11:04:26 +11:00
using System.Collections.ObjectModel ;
using System.Collections.Specialized ;
2012-11-09 19:54:10 -01:00
using System.Linq ;
2014-01-06 11:04:26 +11:00
using System.Reflection ;
2012-11-11 06:53:02 -01:00
using System.Runtime.Serialization ;
2014-01-23 18:44:41 +11:00
using Umbraco.Core.Configuration ;
2012-11-09 14:45:28 -01:00
using Umbraco.Core.Models.EntityBase ;
2014-02-20 22:34:54 +11:00
2012-11-05 14:42:21 -01:00
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// Represents a backoffice user
/// </summary>
/// <remarks>
/// Should be internal until a proper user/membership implementation
/// is part of the roadmap.
/// </remarks>
2012-11-11 06:53:02 -01:00
[Serializable]
[DataContract(IsReference = true)]
2014-02-20 22:34:54 +11:00
public class User : Entity , IUser
2012-11-05 14:42:21 -01:00
{
2012-11-09 19:54:10 -01:00
public User ( IUserType userType )
{
2014-01-06 11:56:53 +11:00
if ( userType = = null ) throw new ArgumentNullException ( "userType" ) ;
2014-01-06 11:04:26 +11:00
_userType = userType ;
2015-03-03 19:37:13 +11:00
_defaultPermissions = _userType . Permissions = = null ? Enumerable . Empty < string > ( ) : new List < string > ( _userType . Permissions ) ;
2014-01-06 11:04:26 +11:00
//Groups = new List<object> { userType };
SessionTimeout = 60 ;
_sectionCollection = new ObservableCollection < string > ( ) ;
_addedSections = new List < string > ( ) ;
_removedSections = new List < string > ( ) ;
2014-01-23 18:44:41 +11:00
_language = GlobalSettings . DefaultUILanguage ;
2014-01-06 11:04:26 +11:00
_sectionCollection . CollectionChanged + = SectionCollectionChanged ;
2014-02-17 11:17:15 +11:00
_isApproved = true ;
_isLockedOut = false ;
2014-02-17 11:48:32 +11:00
_startContentId = - 1 ;
_startMediaId = - 1 ;
2014-03-18 18:41:18 +11:00
//cannot be null
_rawPasswordValue = "" ;
2012-11-09 19:54:10 -01:00
}
2014-03-18 17:08:21 +11:00
public User ( string name , string email , string username , string rawPasswordValue , IUserType userType )
2014-01-06 11:56:53 +11:00
: this ( userType )
{
_name = name ;
_email = email ;
_username = username ;
2014-03-18 17:08:21 +11:00
_rawPasswordValue = rawPasswordValue ;
2014-02-17 11:17:15 +11:00
_isApproved = true ;
_isLockedOut = false ;
2014-02-17 11:48:32 +11:00
_startContentId = - 1 ;
_startMediaId = - 1 ;
2014-01-06 11:56:53 +11:00
}
2014-01-23 18:44:41 +11:00
private IUserType _userType ;
2014-01-06 11:04:26 +11:00
private string _name ;
2015-03-25 10:57:10 +11:00
private string _securityStamp ;
2014-05-09 13:25:18 +10:00
private List < string > _addedSections ;
private List < string > _removedSections ;
private ObservableCollection < string > _sectionCollection ;
2014-01-06 11:04:26 +11:00
private int _sessionTimeout ;
private int _startContentId ;
private int _startMediaId ;
private string _username ;
private string _email ;
2014-03-18 17:08:21 +11:00
private string _rawPasswordValue ;
2014-01-06 11:04:26 +11:00
private bool _isApproved ;
private bool _isLockedOut ;
private string _language ;
2015-03-03 19:37:13 +11:00
private IEnumerable < string > _defaultPermissions ;
2014-01-06 11:04:26 +11:00
private bool _defaultToLiveEditing ;
2015-03-25 10:57:10 +11:00
private static readonly PropertyInfo SecurityStampSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . SecurityStamp ) ;
2014-01-06 11:04:26 +11:00
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper . GetPropertyInfo < User , int > ( x = > x . SessionTimeout ) ;
private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper . GetPropertyInfo < User , int > ( x = > x . StartContentId ) ;
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper . GetPropertyInfo < User , int > ( x = > x . StartMediaId ) ;
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper . GetPropertyInfo < User , IEnumerable < string > > ( x = > x . AllowedSections ) ;
private static readonly PropertyInfo NameSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . Name ) ;
private static readonly PropertyInfo UsernameSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . Username ) ;
private static readonly PropertyInfo EmailSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . Email ) ;
2014-03-18 17:08:21 +11:00
private static readonly PropertyInfo PasswordSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . RawPasswordValue ) ;
2014-01-06 11:04:26 +11:00
private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper . GetPropertyInfo < User , bool > ( x = > x . IsLockedOut ) ;
private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper . GetPropertyInfo < User , bool > ( x = > x . IsApproved ) ;
private static readonly PropertyInfo LanguageSelector = ExpressionHelper . GetPropertyInfo < User , string > ( x = > x . Language ) ;
2015-03-03 19:37:13 +11:00
2014-01-06 11:04:26 +11:00
private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper . GetPropertyInfo < User , bool > ( x = > x . DefaultToLiveEditing ) ;
2014-01-23 18:44:41 +11:00
private static readonly PropertyInfo UserTypeSelector = ExpressionHelper . GetPropertyInfo < User , IUserType > ( x = > x . UserType ) ;
2014-02-20 22:34:54 +11:00
2012-11-05 14:42:21 -01:00
#region Implementation of IMembershipUser
2014-01-06 11:04:26 +11:00
[IgnoreDataMember]
public object ProviderUserKey
{
2014-01-08 18:12:07 +11:00
get { return Id ; }
set { throw new NotSupportedException ( "Cannot set the provider user key for a user" ) ; }
2014-01-06 11:04:26 +11:00
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public string Username
{
get { return _username ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_username = value ;
return _username ;
} , _username , UsernameSelector ) ;
}
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public string Email
{
get { return _email ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_email = value ;
return _email ;
} , _email , EmailSelector ) ;
}
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-03-18 17:08:21 +11:00
public string RawPasswordValue
2014-01-06 11:04:26 +11:00
{
2014-03-18 17:08:21 +11:00
get { return _rawPasswordValue ; }
2014-01-06 11:04:26 +11:00
set
{
SetPropertyValueAndDetectChanges ( o = >
{
2014-03-18 17:08:21 +11:00
_rawPasswordValue = value ;
return _rawPasswordValue ;
} , _rawPasswordValue , PasswordSelector ) ;
2014-01-06 11:04:26 +11:00
}
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public bool IsApproved
{
get { return _isApproved ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_isApproved = value ;
return _isApproved ;
} , _isApproved , IsApprovedSelector ) ;
}
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public bool IsLockedOut
{
get { return _isLockedOut ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_isLockedOut = value ;
return _isLockedOut ;
} , _isLockedOut , IsLockedOutSelector ) ;
}
2014-01-06 15:38:34 +11:00
}
2014-01-06 11:04:26 +11:00
2014-01-07 17:01:22 +11:00
//TODO: Figure out how to support all of this! - we cannot have NotImplementedExceptions because these get used by the IMembershipMemberService<IUser> service so
// we'll just have them as generic get/set which don't interact with the db.
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-01-07 17:01:22 +11:00
public string PasswordQuestion { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-03-18 17:08:21 +11:00
public string RawPasswordAnswerValue { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-02-20 22:34:54 +11:00
public string Comments { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-01-07 17:01:22 +11:00
public DateTime LastLoginDate { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-01-07 17:01:22 +11:00
public DateTime LastPasswordChangeDate { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-01-07 17:01:22 +11:00
public DateTime LastLockoutDate { get ; set ; }
2014-01-06 15:38:34 +11:00
[IgnoreDataMember]
2014-01-07 17:01:22 +11:00
public int FailedPasswordAttempts { get ; set ; }
2014-01-06 11:04:26 +11:00
#endregion
2014-01-23 15:31:49 +11:00
#region Implementation of IUser
2012-11-05 14:42:21 -01:00
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public string Name
{
get { return _name ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
2014-01-23 15:31:49 +11:00
{
_name = value ;
return _name ;
} , _name , NameSelector ) ;
2014-01-06 11:04:26 +11:00
}
}
2012-11-05 14:42:21 -01:00
2014-01-06 11:04:26 +11:00
public IEnumerable < string > AllowedSections
{
get { return _sectionCollection ; }
}
public void RemoveAllowedSection ( string sectionAlias )
{
2014-05-09 15:19:00 +10:00
if ( _sectionCollection . Contains ( sectionAlias ) )
{
_sectionCollection . Remove ( sectionAlias ) ;
}
2014-01-06 11:04:26 +11:00
}
public void AddAllowedSection ( string sectionAlias )
{
if ( _sectionCollection . Contains ( sectionAlias ) = = false )
{
_sectionCollection . Add ( sectionAlias ) ;
}
}
2014-01-23 15:31:49 +11:00
public IProfile ProfileData
{
get { return new UserProfile ( this ) ; }
}
2015-03-25 10:57:10 +11:00
/// <summary>
/// The security stamp used by ASP.Net identity
/// </summary>
public string SecurityStamp
{
get { return _securityStamp ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_securityStamp = value ;
return _securityStamp ;
} , _securityStamp , SecurityStampSelector ) ;
}
}
2014-01-06 11:04:26 +11:00
/// <summary>
/// Used internally to check if we need to add a section in the repository to the db
/// </summary>
internal IEnumerable < string > AddedSections
{
get { return _addedSections ; }
}
/// <summary>
/// Used internally to check if we need to remove a section in the repository to the db
/// </summary>
internal IEnumerable < string > RemovedSections
{
get { return _removedSections ; }
}
/// <summary>
/// Gets or sets the session timeout.
/// </summary>
/// <value>
/// The session timeout.
/// </value>
[DataMember]
public int SessionTimeout
{
get { return _sessionTimeout ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_sessionTimeout = value ;
return _sessionTimeout ;
} , _sessionTimeout , SessionTimeoutSelector ) ;
}
}
/// <summary>
/// Gets or sets the start content id.
/// </summary>
/// <value>
/// The start content id.
/// </value>
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public int StartContentId
{
get { return _startContentId ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_startContentId = value ;
return _startContentId ;
} , _startContentId , StartContentIdSelector ) ;
}
}
/// <summary>
/// Gets or sets the start media id.
/// </summary>
/// <value>
/// The start media id.
/// </value>
[DataMember]
public int StartMediaId
{
get { return _startMediaId ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_startMediaId = value ;
return _startMediaId ;
} , _startMediaId , StartMediaIdSelector ) ;
}
}
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public string Language
{
get { return _language ; }
set
{
SetPropertyValueAndDetectChanges ( o = >
{
_language = value ;
return _language ;
} , _language , LanguageSelector ) ;
}
}
2015-03-03 19:37:13 +11:00
//TODO: This should be a private set
2012-11-11 06:53:02 -01:00
[DataMember]
2014-01-06 11:04:26 +11:00
public IEnumerable < string > DefaultPermissions
{
2015-03-03 19:37:13 +11:00
get { return _defaultPermissions ; }
set { _defaultPermissions = value ; }
2014-01-06 11:04:26 +11:00
}
2012-11-09 19:54:10 -01:00
2012-11-11 06:53:02 -01:00
[IgnoreDataMember]
2014-01-06 11:04:26 +11:00
internal bool DefaultToLiveEditing
2012-11-09 19:54:10 -01:00
{
2014-01-06 11:04:26 +11:00
get { return _defaultToLiveEditing ; }
set
2013-07-03 16:40:22 +10:00
{
2014-01-06 11:04:26 +11:00
SetPropertyValueAndDetectChanges ( o = >
{
_defaultToLiveEditing = value ;
return _defaultToLiveEditing ;
} , _defaultToLiveEditing , DefaultToLiveEditingSelector ) ;
2012-11-09 19:54:10 -01:00
}
}
2014-01-06 11:04:26 +11:00
[IgnoreDataMember]
public IUserType UserType
{
get { return _userType ; }
2014-01-23 18:44:41 +11:00
set
{
if ( value . HasIdentity = = false )
{
throw new InvalidOperationException ( "Cannot assign a User Type that has not been persisted" ) ;
}
SetPropertyValueAndDetectChanges ( o = >
{
_userType = value ;
return _userType ;
} , _userType , UserTypeSelector ) ;
}
2014-01-06 11:04:26 +11:00
}
2012-11-09 19:54:10 -01:00
#endregion
2014-01-06 11:04:26 +11:00
/// <summary>
/// Whenever resetting occurs, clear the remembered add/removed collections, even if
/// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
/// be flagged as dirty.
/// </summary>
/// <param name="rememberPreviouslyChangedProperties"></param>
2014-01-08 16:09:35 +11:00
public override void ResetDirtyProperties ( bool rememberPreviouslyChangedProperties )
2014-01-06 11:04:26 +11:00
{
_addedSections . Clear ( ) ;
_removedSections . Clear ( ) ;
base . ResetDirtyProperties ( rememberPreviouslyChangedProperties ) ;
}
/// <summary>
/// Handles the collection changed event in order for us to flag the AllowedSections property as changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void SectionCollectionChanged ( object sender , NotifyCollectionChangedEventArgs e )
{
OnPropertyChanged ( AllowedSectionsSelector ) ;
if ( e . Action = = NotifyCollectionChangedAction . Add )
{
2014-05-09 13:25:18 +10:00
var item = e . NewItems . Cast < string > ( ) . First ( ) ;
2014-05-09 15:19:00 +10:00
if ( _addedSections . Contains ( item ) = = false )
{
_addedSections . Add ( item ) ;
}
2014-01-06 11:04:26 +11:00
}
else if ( e . Action = = NotifyCollectionChangedAction . Remove )
{
2014-05-09 13:25:18 +10:00
var item = e . OldItems . Cast < string > ( ) . First ( ) ;
2014-05-09 15:19:00 +10:00
if ( _removedSections . Contains ( item ) = = false )
{
_removedSections . Add ( item ) ;
}
2014-01-06 11:04:26 +11:00
}
}
2014-05-09 13:25:18 +10:00
public override object DeepClone ( )
{
var clone = ( User ) base . DeepClone ( ) ;
2015-03-03 18:33:04 +11:00
//turn off change tracking
clone . DisableChangeTracking ( ) ;
2014-05-09 13:25:18 +10:00
//need to create new collections otherwise they'll get copied by ref
clone . _addedSections = new List < string > ( ) ;
clone . _removedSections = new List < string > ( ) ;
2014-05-09 14:01:20 +10:00
clone . _sectionCollection = new ObservableCollection < string > ( _sectionCollection . ToList ( ) ) ;
2015-03-03 19:37:13 +11:00
clone . _defaultPermissions = new List < string > ( _defaultPermissions . ToList ( ) ) ;
2014-05-09 13:25:18 +10:00
//re-create the event handler
clone . _sectionCollection . CollectionChanged + = clone . SectionCollectionChanged ;
2015-03-03 18:33:04 +11:00
//this shouldn't really be needed since we're not tracking
2014-05-09 13:25:18 +10:00
clone . ResetDirtyProperties ( false ) ;
2015-03-03 18:33:04 +11:00
//re-enable tracking
clone . EnableChangeTracking ( ) ;
2014-05-09 13:25:18 +10:00
return clone ;
}
2014-01-23 15:31:49 +11:00
/// <summary>
/// Internal class used to wrap the user in a profile
/// </summary>
private class UserProfile : IProfile
{
private readonly IUser _user ;
public UserProfile ( IUser user )
{
_user = user ;
}
public object Id
{
get { return _user . Id ; }
set { _user . Id = ( int ) value ; }
}
public string Name
{
get { return _user . Name ; }
set { _user . Name = value ; }
}
2014-04-15 20:31:32 +10:00
protected bool Equals ( UserProfile other )
{
return _user . Equals ( other . _user ) ;
}
public override bool Equals ( object obj )
{
if ( ReferenceEquals ( null , obj ) ) return false ;
if ( ReferenceEquals ( this , obj ) ) return true ;
if ( obj . GetType ( ) ! = this . GetType ( ) ) return false ;
return Equals ( ( UserProfile ) obj ) ;
}
public override int GetHashCode ( )
{
return _user . GetHashCode ( ) ;
}
2014-01-23 15:31:49 +11:00
}
2012-11-05 14:42:21 -01:00
}
}