2013-02-03 05:06:11 +06:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Web ;
using System.Web.Security ;
2013-07-31 17:08:56 +10:00
using Newtonsoft.Json.Linq ;
2013-02-03 05:06:11 +06:00
using Umbraco.Core ;
2013-04-04 02:11:31 +06:00
using Umbraco.Core.Cache ;
2013-08-31 11:28:19 +10:00
using Umbraco.Core.Configuration ;
2013-02-03 05:06:11 +06:00
using Umbraco.Core.Logging ;
2013-07-31 18:21:27 +10:00
using Umbraco.Core.Models.Membership ;
2013-07-31 17:08:56 +10:00
using Umbraco.Core.Security ;
2013-07-31 18:21:27 +10:00
using umbraco ;
2013-02-03 05:06:11 +06:00
using umbraco.DataLayer ;
2013-04-09 22:11:12 +06:00
using umbraco.businesslogic.Exceptions ;
2013-07-31 18:21:27 +10:00
using GlobalSettings = Umbraco . Core . Configuration . GlobalSettings ;
using Member = umbraco . cms . businesslogic . member . Member ;
using User = umbraco . BusinessLogic . User ;
2013-02-03 05:06:11 +06:00
namespace Umbraco.Web.Security
{
/// <summary>
/// A utility class used for dealing with security in Umbraco
/// </summary>
2013-07-31 17:08:56 +10:00
public class WebSecurity : DisposableObject
2013-02-03 05:06:11 +06:00
{
2013-07-31 17:08:56 +10:00
private HttpContextBase _httpContext ;
2013-07-31 18:21:27 +10:00
private ApplicationContext _applicationContext ;
2013-06-17 16:03:27 +10:00
2013-07-31 18:21:27 +10:00
public WebSecurity ( HttpContextBase httpContext , ApplicationContext applicationContext )
2013-06-17 16:03:27 +10:00
{
_httpContext = httpContext ;
2013-07-31 18:21:27 +10:00
_applicationContext = applicationContext ;
2013-07-31 17:08:56 +10:00
//This ensures the dispose method is called when the request terminates, though
// we also ensure this happens in the Umbraco module because the UmbracoContext is added to the
// http context items.
_httpContext . DisposeOnPipelineCompleted ( this ) ;
2013-06-17 16:03:27 +10:00
}
2013-07-31 17:08:56 +10:00
2013-02-27 00:19:48 +06:00
/// <summary>
/// Returns true or false if the currently logged in member is authorized based on the parameters provided
/// </summary>
/// <param name="allowAll"></param>
/// <param name="allowTypes"></param>
/// <param name="allowGroups"></param>
/// <param name="allowMembers"></param>
/// <returns></returns>
2013-04-04 23:30:17 +06:00
public bool IsMemberAuthorized (
2013-02-27 00:19:48 +06:00
bool allowAll = false ,
IEnumerable < string > allowTypes = null ,
IEnumerable < string > allowGroups = null ,
IEnumerable < int > allowMembers = null )
{
2013-04-15 12:18:40 -02:00
if ( allowAll )
return true ;
2013-02-27 00:19:48 +06:00
if ( allowTypes = = null )
allowTypes = Enumerable . Empty < string > ( ) ;
if ( allowGroups = = null )
allowGroups = Enumerable . Empty < string > ( ) ;
if ( allowMembers = = null )
allowMembers = Enumerable . Empty < int > ( ) ;
2013-04-15 12:18:40 -02:00
2013-02-27 00:19:48 +06:00
// Allow by default
var allowAction = true ;
2013-04-15 12:18:40 -02:00
// Get member details
var member = Member . GetCurrentMember ( ) ;
if ( member = = null )
{
// If not logged on, not allowed
allowAction = false ;
}
else
2013-02-27 00:19:48 +06:00
{
2013-04-15 12:18:40 -02:00
// If types defined, check member is of one of those types
var allowTypesList = allowTypes as IList < string > ? ? allowTypes . ToList ( ) ;
if ( allowTypesList . Any ( allowType = > allowType ! = string . Empty ) )
2013-02-27 00:19:48 +06:00
{
2013-04-15 12:18:40 -02:00
// Allow only if member's type is in list
allowAction = allowTypesList . Select ( x = > x . ToLowerInvariant ( ) ) . Contains ( member . ContentType . Alias . ToLowerInvariant ( ) ) ;
2013-02-27 00:19:48 +06:00
}
2013-04-15 12:18:40 -02:00
// If groups defined, check member is of one of those groups
var allowGroupsList = allowGroups as IList < string > ? ? allowGroups . ToList ( ) ;
if ( allowAction & & allowGroupsList . Any ( allowGroup = > allowGroup ! = string . Empty ) )
{
2013-08-31 22:52:54 +01:00
// Allow only if member is assigned to a group in the list
2013-04-15 12:18:40 -02:00
var groups = Roles . GetRolesForUser ( member . LoginName ) ;
2013-08-31 22:52:54 +01:00
allowAction = allowGroupsList . Select ( s = > s . ToLowerInvariant ( ) ) . Intersect ( groups . Select ( myGroup = > myGroup . ToLowerInvariant ( ) ) ) . Any ( ) ;
2013-04-15 12:18:40 -02:00
}
2013-02-27 00:19:48 +06:00
2013-04-15 12:18:40 -02:00
// If specific members defined, check member is of one of those
if ( allowAction & & allowMembers . Any ( ) )
{
2013-08-31 22:52:54 +01:00
// Allow only if member's Id is in the list
2013-04-15 12:18:40 -02:00
allowAction = allowMembers . Contains ( member . Id ) ;
2013-02-27 00:19:48 +06:00
}
}
2013-04-15 12:18:40 -02:00
2013-02-27 00:19:48 +06:00
return allowAction ;
}
2013-02-03 05:06:11 +06:00
2013-04-15 12:18:40 -02:00
private static readonly int UmbracoTimeOutInMinutes = GlobalSettings . TimeOutInMinutes ;
2013-02-03 05:06:11 +06:00
2013-08-09 13:24:26 +10:00
private IUser _currentUser ;
2013-04-04 23:30:17 +06:00
2013-02-03 05:06:11 +06:00
/// <summary>
/// Gets the current user.
/// </summary>
/// <value>The current user.</value>
2013-08-09 13:24:26 +10:00
internal IUser CurrentUser
2013-02-03 05:06:11 +06:00
{
get
{
2013-04-04 23:30:17 +06:00
//only load it once per instance!
2013-07-31 17:08:56 +10:00
if ( _currentUser = = null )
{
var id = GetUserId ( ) ;
if ( id = = - 1 )
{
return null ;
}
2013-08-09 13:24:26 +10:00
_currentUser = _applicationContext . Services . UserService . GetUserById ( id ) ;
2013-07-31 17:08:56 +10:00
}
return _currentUser ;
2013-02-03 05:06:11 +06:00
}
}
/// <summary>
/// Logs a user in.
/// </summary>
2013-04-04 23:30:17 +06:00
/// <param name="userId">The user Id</param>
public void PerformLogin ( int userId )
2013-02-03 05:06:11 +06:00
{
2013-07-31 18:21:27 +10:00
var user = _applicationContext . Services . UserService . GetUserById ( userId ) ;
2013-07-31 17:08:56 +10:00
PerformLogin ( user ) ;
}
2013-02-03 05:06:11 +06:00
2013-07-31 18:21:27 +10:00
internal void PerformLogin ( IUser user )
2013-07-31 17:08:56 +10:00
{
_httpContext . CreateUmbracoAuthTicket ( new UserData
{
Id = user . Id ,
2013-07-31 18:21:27 +10:00
AllowedApplications = user . AllowedSections . ToArray ( ) ,
2013-07-31 17:08:56 +10:00
RealName = user . Name ,
//currently we only have one user type!
Roles = new [ ] { user . UserType . Alias } ,
2013-07-31 18:21:27 +10:00
StartContentNode = user . StartContentId ,
2013-07-31 17:08:56 +10:00
StartMediaNode = user . StartMediaId ,
2013-07-31 18:21:27 +10:00
Username = user . Username ,
Culture = ui . Culture ( user . Language )
2013-07-31 17:08:56 +10:00
} ) ;
2013-07-31 18:21:27 +10:00
2013-07-31 17:08:56 +10:00
LogHelper . Info < WebSecurity > ( "User Id: {0} logged in" , ( ) = > user . Id ) ;
2013-02-03 05:06:11 +06:00
}
/// <summary>
/// Clears the current login for the currently logged in user
/// </summary>
2013-04-04 23:30:17 +06:00
public void ClearCurrentLogin ( )
2013-02-03 05:06:11 +06:00
{
2013-07-31 17:08:56 +10:00
_httpContext . UmbracoLogout ( ) ;
2013-02-03 05:06:11 +06:00
}
2013-04-04 23:30:17 +06:00
public void RenewLoginTimeout ( )
2013-02-03 05:06:11 +06:00
{
2013-10-15 18:46:44 +11:00
_httpContext . RenewUmbracoAuthTicket ( ) ;
2013-02-03 05:06:11 +06:00
}
2013-04-09 22:11:12 +06:00
/// <summary>
/// Validates credentials for a back office user
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
internal bool ValidateBackOfficeCredentials ( string username , string password )
{
2013-09-25 19:23:41 +10:00
var membershipProvider = Membership . Providers [ UmbracoConfig . For . UmbracoSettings ( ) . Providers . DefaultBackOfficeUserProvider ] ;
2013-04-15 12:18:40 -02:00
return membershipProvider ! = null & & membershipProvider . ValidateUser ( username , password ) ;
2013-04-09 22:11:12 +06:00
}
2013-09-16 14:50:56 +02:00
/// <summary>
/// Changes password for a back office user
/// </summary>
2013-09-17 00:33:02 +10:00
/// <param name="oldpassword"></param>
/// <param name="newpassword"></param>
2013-09-16 14:50:56 +02:00
/// <returns></returns>
internal bool ChangePassword ( string oldpassword , string newpassword )
{
2013-09-25 19:23:41 +10:00
var membershipProvider = Membership . Providers [ UmbracoConfig . For . UmbracoSettings ( ) . Providers . DefaultBackOfficeUserProvider ] ;
2013-09-16 14:50:56 +02:00
return membershipProvider . GetUser ( CurrentUser . Username , true ) . ChangePassword ( oldpassword , newpassword ) ;
}
2013-04-04 23:30:17 +06:00
/// <summary>
/// Validates the user node tree permissions.
/// </summary>
/// <param name="umbracoUser"></param>
/// <param name="path">The path.</param>
/// <param name="action">The action.</param>
/// <returns></returns>
internal bool ValidateUserNodeTreePermissions ( User umbracoUser , string path , string action )
{
var permissions = umbracoUser . GetPermissions ( path ) ;
2013-04-15 12:18:40 -02:00
if ( permissions . IndexOf ( action , StringComparison . Ordinal ) > - 1 & & ( path . Contains ( "-20" ) | | ( "," + path + "," ) . Contains ( "," + umbracoUser . StartNodeId + "," ) ) )
2013-04-04 23:30:17 +06:00
return true ;
var user = umbracoUser ;
LogHelper . Info < WebSecurity > ( "User {0} has insufficient permissions in UmbracoEnsuredPage: '{1}', '{2}', '{3}'" , ( ) = > user . Name , ( ) = > path , ( ) = > permissions , ( ) = > action ) ;
return false ;
}
2013-04-09 22:11:12 +06:00
/// <summary>
/// Validates the current user to see if they have access to the specified app
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
internal bool ValidateUserApp ( string app )
{
//if it is empty, don't validate
if ( app . IsNullOrWhiteSpace ( ) )
{
return true ;
}
2013-08-09 13:24:26 +10:00
var userApps = _applicationContext . Services . UserService . GetUserSections ( CurrentUser ) ;
return userApps . Any ( uApp = > uApp . InvariantEquals ( app ) ) ;
2013-04-09 22:11:12 +06:00
}
2013-07-31 17:08:56 +10:00
internal void UpdateLogin ( )
2013-02-03 05:06:11 +06:00
{
2013-10-15 18:46:44 +11:00
_httpContext . RenewUmbracoAuthTicket ( ) ;
2013-02-03 05:06:11 +06:00
}
2013-07-31 17:08:56 +10:00
internal long GetTimeout ( )
2013-02-03 05:06:11 +06:00
{
2013-07-31 17:08:56 +10:00
var ticket = _httpContext . GetUmbracoAuthTicket ( ) ;
var ticks = ticket . Expiration . Ticks - DateTime . Now . Ticks ;
return ticks ;
2013-02-03 05:06:11 +06:00
}
2013-07-31 17:08:56 +10:00
2013-02-03 05:06:11 +06:00
/// <summary>
/// Gets the user id.
/// </summary>
2013-07-31 17:08:56 +10:00
/// <param name="umbracoUserContextId">This is not used</param>
2013-02-03 05:06:11 +06:00
/// <returns></returns>
2013-07-31 17:08:56 +10:00
[Obsolete("This method is no longer used, use the GetUserId() method without parameters instead")]
2013-04-04 23:30:17 +06:00
public int GetUserId ( string umbracoUserContextId )
2013-07-31 17:08:56 +10:00
{
return GetUserId ( ) ;
}
2013-04-04 02:11:31 +06:00
2013-07-31 17:08:56 +10:00
/// <summary>
/// Gets the currnet user's id.
/// </summary>
/// <returns></returns>
public int GetUserId ( )
{
var identity = _httpContext . GetCurrentIdentity ( ) ;
if ( identity = = null )
2013-04-04 02:11:31 +06:00
return - 1 ;
2013-07-31 18:28:18 +10:00
return Convert . ToInt32 ( identity . Id ) ;
2013-02-03 05:06:11 +06:00
}
/// <summary>
/// Validates the user context ID.
/// </summary>
2013-07-31 17:08:56 +10:00
/// <param name="currentUmbracoUserContextId">This doesn't do anything</param>
2013-02-03 05:06:11 +06:00
/// <returns></returns>
2013-07-31 17:08:56 +10:00
[Obsolete("This method is no longer used, use the ValidateCurrentUser() method instead")]
2013-04-04 23:30:17 +06:00
public bool ValidateUserContextId ( string currentUmbracoUserContextId )
2013-02-03 05:06:11 +06:00
{
2013-07-31 17:08:56 +10:00
return ValidateCurrentUser ( ) ;
}
2013-02-03 05:06:11 +06:00
2013-07-31 17:08:56 +10:00
/// <summary>
/// Validates the currently logged in user and ensures they are not timed out
/// </summary>
/// <returns></returns>
public bool ValidateCurrentUser ( )
{
var ticket = _httpContext . GetUmbracoAuthTicket ( ) ;
if ( ticket ! = null )
{
if ( ticket . Expired = = false )
2013-02-03 05:06:11 +06:00
{
return true ;
}
}
return false ;
}
2013-04-04 23:30:17 +06:00
/// <summary>
/// Validates the current user
/// </summary>
2013-04-09 22:11:12 +06:00
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
2013-04-04 23:30:17 +06:00
/// <returns></returns>
2013-07-31 17:08:56 +10:00
internal ValidateRequestAttempt ValidateCurrentUser ( bool throwExceptions )
2013-04-04 23:30:17 +06:00
{
2013-07-31 17:08:56 +10:00
var ticket = _httpContext . GetUmbracoAuthTicket ( ) ;
2013-04-04 23:30:17 +06:00
2013-07-31 17:08:56 +10:00
if ( ticket ! = null )
{
if ( ticket . Expired = = false )
2013-04-04 23:30:17 +06:00
{
2013-08-09 13:45:57 +10:00
var user = CurrentUser ;
2013-04-04 23:30:17 +06:00
// Check for console access
2013-09-25 17:57:44 +10:00
if ( user . IsLockedOut | | ( user . NoConsole & & GlobalSettings . RequestIsInUmbracoApplication ( _httpContext ) ) )
2013-04-04 23:30:17 +06:00
{
2013-04-09 22:11:12 +06:00
if ( throwExceptions ) throw new ArgumentException ( "You have no priviledges to the umbraco console. Please contact your administrator" ) ;
return ValidateRequestAttempt . FailedNoPrivileges ;
2013-04-04 23:30:17 +06:00
}
2013-07-31 17:08:56 +10:00
UpdateLogin ( ) ;
2013-04-09 22:11:12 +06:00
return ValidateRequestAttempt . Success ;
2013-04-04 23:30:17 +06:00
}
2013-04-09 22:11:12 +06:00
if ( throwExceptions ) throw new ArgumentException ( "User has timed out!!" ) ;
return ValidateRequestAttempt . FailedTimedOut ;
}
2013-07-31 17:08:56 +10:00
2013-04-09 22:11:12 +06:00
if ( throwExceptions ) throw new InvalidOperationException ( "The user has no umbraco contextid - try logging in" ) ;
return ValidateRequestAttempt . FailedNoContextId ;
}
/// <summary>
/// Authorizes the full request, checks for SSL and validates the current user
/// </summary>
/// <param name="throwExceptions">set to true if you want exceptions to be thrown if failed</param>
/// <returns></returns>
2013-06-17 16:03:27 +10:00
internal ValidateRequestAttempt AuthorizeRequest ( bool throwExceptions = false )
2013-04-09 22:11:12 +06:00
{
// check for secure connection
2013-06-17 16:03:27 +10:00
if ( GlobalSettings . UseSSL & & _httpContext . Request . IsSecureConnection = = false )
2013-04-09 22:11:12 +06:00
{
2013-04-15 12:18:40 -02:00
if ( throwExceptions ) throw new UserAuthorizationException ( "This installation requires a secure connection (via SSL). Please update the URL to include https://" ) ;
2013-04-09 22:11:12 +06:00
return ValidateRequestAttempt . FailedNoSsl ;
2013-04-04 23:30:17 +06:00
}
2013-06-17 16:03:27 +10:00
return ValidateCurrentUser ( throwExceptions ) ;
2013-04-09 22:11:12 +06:00
}
/// <summary>
/// Checks if the specified user as access to the app
/// </summary>
/// <param name="app"></param>
/// <param name="user"></param>
/// <returns></returns>
2013-08-09 13:45:57 +10:00
internal bool UserHasAppAccess ( string app , IUser user )
{
var apps = _applicationContext . Services . UserService . GetUserSections ( user ) ;
return apps . Any ( uApp = > uApp . InvariantEquals ( app ) ) ;
}
[Obsolete("Do not use this method if you don't have to, use the overload with IUser instead")]
2013-04-09 22:11:12 +06:00
internal bool UserHasAppAccess ( string app , User user )
{
return user . Applications . Any ( uApp = > uApp . alias = = app ) ;
}
/// <summary>
/// Checks if the specified user by username as access to the app
/// </summary>
/// <param name="app"></param>
/// <param name="username"></param>
/// <returns></returns>
internal bool UserHasAppAccess ( string app , string username )
{
2013-08-09 13:45:57 +10:00
var user = _applicationContext . Services . UserService . GetUserByUserName ( username ) ;
if ( user = = null )
{
return false ;
}
return UserHasAppAccess ( app , user ) ;
2013-04-04 23:30:17 +06:00
}
2013-07-31 17:08:56 +10:00
[Obsolete("This is no longer used at all, it will always return a new GUID though if a user is logged in")]
2013-04-04 23:30:17 +06:00
public string UmbracoUserContextId
2013-07-31 17:08:56 +10:00
{
2013-02-03 05:06:11 +06:00
get
{
2013-07-31 17:08:56 +10:00
return _httpContext . GetUmbracoAuthTicket ( ) = = null ? "" : Guid . NewGuid ( ) . ToString ( ) ;
2013-02-03 05:06:11 +06:00
}
set
{
}
}
2013-07-31 17:08:56 +10:00
protected override void DisposeResources ( )
{
_httpContext = null ;
2013-07-31 18:21:27 +10:00
_applicationContext = null ;
2013-07-31 17:08:56 +10:00
}
2013-02-03 05:06:11 +06:00
}
}