From 16746c8548698aeb1868237882b52a9cfb428ce9 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 9 Apr 2013 22:11:12 +0600 Subject: [PATCH] Updates authorized web services/http handlers to use WebSecurity and implements the new base class properties available in 6.1. Updates WebSecurity with a few more helpful methods. --- src/Umbraco.Web.UI/install/Default.aspx.cs | 2 +- ...erAttempt.cs => ValidateRequestAttempt.cs} | 5 +- src/Umbraco.Web/Security/WebSecurity.cs | 94 ++++++++++++++++--- .../UI/Pages/UmbracoEnsuredPage.cs | 44 ++------- src/Umbraco.Web/Umbraco.Web.csproj | 4 +- .../UmbracoAuthorizedHttpHandler.cs | 60 +++--------- .../UmbracoAuthorizedWebService.cs | 61 +++--------- .../WebServices/UmbracoHttpHandler.cs | 38 +++++--- .../WebServices/UmbracoWebService.cs | 38 +++++--- .../umbraco/webservices/nodeSorter.asmx.cs | 9 +- 10 files changed, 180 insertions(+), 175 deletions(-) rename src/Umbraco.Web/Security/{ValidateUserAttempt.cs => ValidateRequestAttempt.cs} (54%) diff --git a/src/Umbraco.Web.UI/install/Default.aspx.cs b/src/Umbraco.Web.UI/install/Default.aspx.cs index 3c66e5105b..a53429c2b6 100644 --- a/src/Umbraco.Web.UI/install/Default.aspx.cs +++ b/src/Umbraco.Web.UI/install/Default.aspx.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.UI.Install { var result = Security.ValidateCurrentUser(new HttpContextWrapper(Context)); - if (result == ValidateUserAttempt.FailedTimedOut || result == ValidateUserAttempt.FailedNoPrivileges) + if (result == ValidateRequestAttempt.FailedTimedOut || result == ValidateRequestAttempt.FailedNoPrivileges) { Response.Redirect(SystemDirectories.Umbraco + "/logout.aspx?redir=" + Server.UrlEncode(Request.RawUrl)); } diff --git a/src/Umbraco.Web/Security/ValidateUserAttempt.cs b/src/Umbraco.Web/Security/ValidateRequestAttempt.cs similarity index 54% rename from src/Umbraco.Web/Security/ValidateUserAttempt.cs rename to src/Umbraco.Web/Security/ValidateRequestAttempt.cs index 942e0b16e6..1ec1dca2f0 100644 --- a/src/Umbraco.Web/Security/ValidateUserAttempt.cs +++ b/src/Umbraco.Web/Security/ValidateRequestAttempt.cs @@ -1,10 +1,11 @@ namespace Umbraco.Web.Security { - internal enum ValidateUserAttempt + internal enum ValidateRequestAttempt { Success, FailedNoPrivileges, FailedTimedOut, - FailedNoContextId + FailedNoContextId, + FailedNoSsl } } \ No newline at end of file diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 0a4f58e39a..1a5f344d9c 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using umbraco.BusinessLogic; using umbraco.DataLayer; +using umbraco.businesslogic.Exceptions; using umbraco.cms.businesslogic.member; namespace Umbraco.Web.Security @@ -158,6 +159,17 @@ namespace Umbraco.Web.Security SqlHelper.CreateParameter("@contextId", UmbracoUserContextId)); } + /// + /// Validates credentials for a back office user + /// + /// + /// + /// + internal bool ValidateBackOfficeCredentials(string username, string password) + { + return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password); + } + /// /// Validates the user node tree permissions. /// @@ -176,6 +188,21 @@ namespace Umbraco.Web.Security return false; } + /// + /// Validates the current user to see if they have access to the specified app + /// + /// + /// + internal bool ValidateUserApp(string app) + { + //if it is empty, don't validate + if (app.IsNullOrWhiteSpace()) + { + return true; + } + return CurrentUser.Applications.Any(uApp => uApp.alias == app); + } + internal void UpdateLogin(long timeout) { // only call update if more than 1/10 of the timeout has passed @@ -260,8 +287,9 @@ namespace Umbraco.Web.Security /// Validates the current user /// /// + /// set to true if you want exceptions to be thrown if failed /// - internal ValidateUserAttempt ValidateCurrentUser(HttpContextBase httpContext) + internal ValidateRequestAttempt ValidateCurrentUser(HttpContextBase httpContext, bool throwExceptions = false) { if (UmbracoUserContextId != "") { @@ -275,28 +303,72 @@ namespace Umbraco.Web.Security // Check for console access if (user.Disabled || (user.NoConsole && GlobalSettings.RequestIsInUmbracoApplication(httpContext) && !GlobalSettings.RequestIsLiveEditRedirector(httpContext))) { - return ValidateUserAttempt.FailedNoPrivileges; - //throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator"); + if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator"); + return ValidateRequestAttempt.FailedNoPrivileges; } UpdateLogin(timeout); - return ValidateUserAttempt.Success; + return ValidateRequestAttempt.Success; } - return ValidateUserAttempt.FailedTimedOut; - //throw new ArgumentException("User has timed out!!"); + if (throwExceptions) throw new ArgumentException("User has timed out!!"); + return ValidateRequestAttempt.FailedTimedOut; } - return ValidateUserAttempt.FailedNoContextId; - //throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); + if (throwExceptions) throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); + return ValidateRequestAttempt.FailedNoContextId; + } + + /// + /// Authorizes the full request, checks for SSL and validates the current user + /// + /// + /// set to true if you want exceptions to be thrown if failed + /// + internal ValidateRequestAttempt AuthorizeRequest(HttpContextBase httpContext, bool throwExceptions = false) + { + // check for secure connection + if (GlobalSettings.UseSSL && !httpContext.Request.IsSecureConnection) + { + if (throwExceptions) throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://"); + return ValidateRequestAttempt.FailedNoSsl; + } + return ValidateCurrentUser(httpContext, throwExceptions); + } + + /// + /// Checks if the specified user as access to the app + /// + /// + /// + /// + internal bool UserHasAppAccess(string app, User user) + { + return user.Applications.Any(uApp => uApp.alias == app); + } + + /// + /// Checks if the specified user by username as access to the app + /// + /// + /// + /// + internal bool UserHasAppAccess(string app, string username) + { + var uid = User.getUserId(username); + if (uid < 0) return false; + var usr = User.GetUser(uid); + if (usr == null) return false; + return UserHasAppAccess(app, usr); } - //TODO: Clean this up!! We also have extension methods in StringExtensions for decrypting/encrypting in med trust - // ... though an existing cookie may fail decryption, in that case they'd just get logged out. no problems. - /// /// Gets or sets the umbraco user context ID. /// /// The umbraco user context ID. public string UmbracoUserContextId { + + //TODO: Clean this up!! We also have extension methods in StringExtensions for decrypting/encrypting in med trust + // ... though an existing cookie may fail decryption, in that case they'd just get logged out. no problems. + get { // zb-00004 #29956 : refactor cookies names & handling diff --git a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs index e3ea63b57b..3979415dc7 100644 --- a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs +++ b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs @@ -28,9 +28,10 @@ namespace Umbraco.Web.UI.Pages try { - ValidateUser(); + Security.ValidateCurrentUser(new HttpContextWrapper(Context), true); + _hasValidated = true; - if (!ValidateUserApp(CurrentApp)) + if (!Security.ValidateUserApp(CurrentApp)) { var ex = new UserAuthorizationException(String.Format("The current user doesn't have access to the section/app '{0}'", CurrentApp)); LogHelper.Error(String.Format("Tried to access '{0}'", CurrentApp), ex); @@ -68,37 +69,6 @@ namespace Umbraco.Web.UI.Pages /// If true then umbraco will force any window/frame to reload umbraco in the main window /// protected bool RedirectToUmbraco { get; set; } - - /// - /// Validates the user for access to a certain application - /// - /// The application alias. - /// - private bool ValidateUserApp(string app) - { - //if it is empty, don't validate - if (app.IsNullOrWhiteSpace()) - { - return true; - } - return Security.CurrentUser.Applications.Any(uApp => uApp.alias == app); - } - - private void ValidateUser() - { - //validate the current user, if failed then throw exceptions - var attempt = Security.ValidateCurrentUser(new HttpContextWrapper(Context)); - _hasValidated = true; - switch (attempt) - { - case ValidateUserAttempt.FailedNoPrivileges: - throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator"); - case ValidateUserAttempt.FailedTimedOut: - throw new ArgumentException("User has timed out!!"); - case ValidateUserAttempt.FailedNoContextId: - throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); - } - } /// /// Returns the current user @@ -107,7 +77,13 @@ namespace Umbraco.Web.UI.Pages { get { - if (!_hasValidated) ValidateUser(); + //throw exceptions if not valid (true) + if (!_hasValidated) + { + Security.ValidateCurrentUser(new HttpContextWrapper(Context), true); + _hasValidated = true; + } + return Security.CurrentUser; } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7e681679bd..a4f8fd5afa 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -342,7 +342,7 @@ - + @@ -471,7 +471,7 @@ - ASPXCodeBehind + ASPXCodeBehind ASPXCodeBehind diff --git a/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs b/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs index 4524f1e47c..a16b37a468 100644 --- a/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs +++ b/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Web; using System.Web.Security; using Umbraco.Core; +using Umbraco.Web.Security; using umbraco; using umbraco.BasePages; using umbraco.BusinessLogic; @@ -22,11 +23,7 @@ namespace Umbraco.Web.WebServices { } - //IMPORTANT NOTE: !! All of these security bits and pieces have been moved in to one centralized class - // in 6.1 called WebSecurity. All this logic is all here temporarily! - - private User _user; - private readonly InnerPage _page = new InnerPage(); + private bool _hasValidated = false; /// /// Checks if the umbraco context id is valid @@ -35,7 +32,7 @@ namespace Umbraco.Web.WebServices /// protected bool ValidateUserContextId(string currentUmbracoUserContextId) { - return BasePage.ValidateUserContextID(currentUmbracoUserContextId); + return UmbracoContext.Security.ValidateUserContextId(currentUmbracoUserContextId); } /// @@ -46,7 +43,7 @@ namespace Umbraco.Web.WebServices /// protected bool ValidateCredentials(string username, string password) { - return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password); + return UmbracoContext.Security.ValidateBackOfficeCredentials(username, password); } /// @@ -79,7 +76,7 @@ namespace Umbraco.Web.WebServices /// protected bool UserHasAppAccess(string app, User user) { - return user.Applications.Any(uApp => uApp.alias == app); + return Security.UserHasAppAccess(app, user); } /// @@ -90,11 +87,7 @@ namespace Umbraco.Web.WebServices /// protected bool UserHasAppAccess(string app, string username) { - var uid = global::umbraco.BusinessLogic.User.getUserId(username); - if (uid < 0) return false; - var usr = global::umbraco.BusinessLogic.User.GetUser(uid); - if (usr == null) return false; - return UserHasAppAccess(app, usr); + return Security.UserHasAppAccess(app, username); } /// @@ -104,30 +97,8 @@ namespace Umbraco.Web.WebServices /// protected bool AuthorizeRequest(bool throwExceptions = false) { - // check for secure connection - if (GlobalSettings.UseSSL && !HttpContext.Current.Request.IsSecureConnection) - { - if (throwExceptions) - throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://"); - return false; - } - - try - { - return UmbracoUser != null; - } - catch (ArgumentException) - { - if (throwExceptions) throw; - //an exception will occur if the user is not valid inside of _page.getUser(); - return false; - } - catch (InvalidOperationException) - { - if (throwExceptions) throw; - //an exception will occur if the user is not valid inside of _page.getUser(); - return false; - } + var result = Security.AuthorizeRequest(new HttpContextWrapper(HttpContext.Current), throwExceptions); + return result == ValidateRequestAttempt.Success; } /// @@ -137,17 +108,14 @@ namespace Umbraco.Web.WebServices { get { - return _user ?? (_user = _page.getUser()); + if (!_hasValidated) + { + Security.ValidateCurrentUser(new HttpContextWrapper(HttpContext.Current)); + _hasValidated = true; + } + return Security.CurrentUser; } } - /// - /// Used to validate, thie is temporary, in 6.1 we have the WebSecurity class which does all - /// authorization stuff for us. - /// - private class InnerPage : BasePage - { - - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs b/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs index ac55ebecbc..a532936e77 100644 --- a/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs +++ b/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs @@ -5,6 +5,7 @@ using System.Text; using System.Web; using System.Web.Security; using Umbraco.Core.Configuration; +using Umbraco.Web.Security; using umbraco.BasePages; using umbraco.BusinessLogic; using Umbraco.Core; @@ -26,12 +27,8 @@ namespace Umbraco.Web.WebServices : base(umbracoContext) { } - - //IMPORTANT NOTE: !! All of these security bits and pieces have been moved in to one centralized class - // in 6.1 called WebSecurity. All this logic is all here temporarily! - private User _user; - private readonly InnerPage _page = new InnerPage(); + private bool _hasValidated = false; /// /// Checks if the umbraco context id is valid @@ -40,7 +37,7 @@ namespace Umbraco.Web.WebServices /// protected bool ValidateUserContextId(string currentUmbracoUserContextId) { - return BasePage.ValidateUserContextID(currentUmbracoUserContextId); + return UmbracoContext.Security.ValidateUserContextId(currentUmbracoUserContextId); } /// @@ -51,7 +48,7 @@ namespace Umbraco.Web.WebServices /// protected bool ValidateCredentials(string username, string password) { - return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password); + return UmbracoContext.Security.ValidateBackOfficeCredentials(username, password); } /// @@ -84,7 +81,7 @@ namespace Umbraco.Web.WebServices /// protected bool UserHasAppAccess(string app, User user) { - return user.Applications.Any(uApp => uApp.alias == app); + return Security.UserHasAppAccess(app, user); } /// @@ -95,11 +92,7 @@ namespace Umbraco.Web.WebServices /// protected bool UserHasAppAccess(string app, string username) { - var uid = global::umbraco.BusinessLogic.User.getUserId(username); - if (uid < 0) return false; - var usr = global::umbraco.BusinessLogic.User.GetUser(uid); - if (usr == null) return false; - return UserHasAppAccess(app, usr); + return Security.UserHasAppAccess(app, username); } /// @@ -109,30 +102,8 @@ namespace Umbraco.Web.WebServices /// protected bool AuthorizeRequest(bool throwExceptions = false) { - // check for secure connection - if (GlobalSettings.UseSSL && !HttpContext.Current.Request.IsSecureConnection) - { - if (throwExceptions) - throw new UserAuthorizationException("This installation requires a secure connection (via SSL). Please update the URL to include https://"); - return false; - } - - try - { - return UmbracoUser != null; - } - catch (ArgumentException) - { - if (throwExceptions) throw; - //an exception will occur if the user is not valid inside of _page.getUser(); - return false; - } - catch (InvalidOperationException) - { - if (throwExceptions) throw; - //an exception will occur if the user is not valid inside of _page.getUser(); - return false; - } + var result = Security.AuthorizeRequest(new HttpContextWrapper(Context), throwExceptions); + return result == ValidateRequestAttempt.Success; } /// @@ -142,18 +113,14 @@ namespace Umbraco.Web.WebServices { get { - return _user ?? (_user = _page.getUser()); + if (!_hasValidated) + { + Security.ValidateCurrentUser(new HttpContextWrapper(Context)); + _hasValidated = true; + } + return Security.CurrentUser; } } - /// - /// Used to validate, thie is temporary, in 6.1 we have the WebSecurity class which does all - /// authorization stuff for us. - /// - private class InnerPage : BasePage - { - - } - } } diff --git a/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs b/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs index 28e7619b04..b7c8344788 100644 --- a/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs +++ b/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs @@ -3,6 +3,8 @@ using System.Web; using System.Web.Mvc; using System.Web.Routing; using Umbraco.Core; +using Umbraco.Core.Services; +using Umbraco.Web.Security; namespace Umbraco.Web.WebServices { @@ -55,20 +57,28 @@ namespace Umbraco.Web.WebServices get { return _url ?? (_url = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()))); } } - ///// - ///// Returns a ServiceContext - ///// - //public ServiceContext Services - //{ - // get { return ApplicationContext.Services; } - //} + /// + /// Returns a ServiceContext + /// + public ServiceContext Services + { + get { return ApplicationContext.Services; } + } - ///// - ///// Returns a DatabaseContext - ///// - //public DatabaseContext DatabaseContext - //{ - // get { return ApplicationContext.DatabaseContext; } - //} + /// + /// Returns a DatabaseContext + /// + public DatabaseContext DatabaseContext + { + get { return ApplicationContext.DatabaseContext; } + } + + /// + /// Returns a WebSecurity instance + /// + public WebSecurity Security + { + get { return UmbracoContext.Security; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/WebServices/UmbracoWebService.cs b/src/Umbraco.Web/WebServices/UmbracoWebService.cs index 1e7ac345cd..5f7dc7da5d 100644 --- a/src/Umbraco.Web/WebServices/UmbracoWebService.cs +++ b/src/Umbraco.Web/WebServices/UmbracoWebService.cs @@ -4,6 +4,8 @@ using System.Web.Mvc; using System.Web.Routing; using System.Web.Services; using Umbraco.Core; +using Umbraco.Core.Services; +using Umbraco.Web.Security; namespace Umbraco.Web.WebServices { @@ -56,20 +58,28 @@ namespace Umbraco.Web.WebServices get { return _url ?? (_url = new UrlHelper(new RequestContext(new HttpContextWrapper(Context), new RouteData()))); } } - ///// - ///// Returns a ServiceContext - ///// - //public ServiceContext Services - //{ - // get { return ApplicationContext.Services; } - //} + /// + /// Returns a ServiceContext + /// + public ServiceContext Services + { + get { return ApplicationContext.Services; } + } - ///// - ///// Returns a DatabaseContext - ///// - //public DatabaseContext DatabaseContext - //{ - // get { return ApplicationContext.DatabaseContext; } - //} + /// + /// Returns a DatabaseContext + /// + public DatabaseContext DatabaseContext + { + get { return ApplicationContext.DatabaseContext; } + } + + /// + /// Returns a WebSecurity instance + /// + public WebSecurity Security + { + get { return UmbracoContext.Security; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs index 0ef604cb63..19847e33f2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs @@ -84,8 +84,9 @@ namespace umbraco.presentation.webservices if (SortOrder.Trim().Length <= 0) return; var tmp = SortOrder.Split(','); - var isContent = Context.Request.GetItemAsString("app") == Constants.Applications.Content | helper.Request("app") == ""; - var isMedia = Context.Request.GetItemAsString("app") == Constants.Applications.Media; + var isContent = Context.Request.GetItemAsString("app") == Constants.Applications.Content | helper.Request("app") == ""; + var isMedia = Context.Request.GetItemAsString("app") == Constants.Applications.Media; + //ensure user is authorized for the app requested if (isContent && !AuthorizeRequest(DefaultApps.content.ToString())) return; if (isMedia && !AuthorizeRequest(DefaultApps.media.ToString())) return; @@ -103,7 +104,7 @@ namespace umbraco.presentation.webservices // refresh the xml for the sorting to work if (published) { - document.SaveAndPublish(global::Umbraco.Web.UmbracoContext.Current.UmbracoUser); + document.SaveAndPublish(UmbracoUser); document.refreshXmlSortOrder(); } } @@ -140,7 +141,7 @@ namespace umbraco.presentation.webservices } // fire actionhandler, check for content - if ((helper.Request("app") == Constants.Applications.Content | helper.Request("app") == "") && ParentId > 0) + if ((helper.Request("app") == Constants.Applications.Content | helper.Request("app") == "") && ParentId > 0) BusinessLogic.Actions.Action.RunActionHandlers(new Document(ParentId), ActionSort.Instance); } catch (Exception ex)