From 25b9ca3b0e1c805a16214dc84d0d4e19ed994ca1 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 9 Apr 2013 07:01:43 +0600 Subject: [PATCH] Works on #U4-2078 - adds new base classes for HttpHandlers just like for web services. --- src/Umbraco.Web/Umbraco.Web.csproj | 2 + .../UmbracoAuthorizedHttpHandler.cs | 153 ++++++++++++++++++ .../UmbracoAuthorizedWebService.cs | 5 +- .../WebServices/UmbracoHttpHandler.cs | 74 +++++++++ .../webservices/TreeDataService.ashx.cs | 26 +-- .../UltimatePickerAutoCompleteHandler.ashx.cs | 79 ++++----- 6 files changed, 290 insertions(+), 49 deletions(-) create mode 100644 src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs create mode 100644 src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c6c7b482b2..c39de1963a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1816,9 +1816,11 @@ Reference.map + Component + Component diff --git a/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs b/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs new file mode 100644 index 0000000000..4524f1e47c --- /dev/null +++ b/src/Umbraco.Web/WebServices/UmbracoAuthorizedHttpHandler.cs @@ -0,0 +1,153 @@ +using System; +using System.Linq; +using System.Web; +using System.Web.Security; +using Umbraco.Core; +using umbraco; +using umbraco.BasePages; +using umbraco.BusinessLogic; +using umbraco.businesslogic.Exceptions; + +namespace Umbraco.Web.WebServices +{ + public abstract class UmbracoAuthorizedHttpHandler : UmbracoHttpHandler + { + protected UmbracoAuthorizedHttpHandler() + : base() + { + } + + protected UmbracoAuthorizedHttpHandler(UmbracoContext umbracoContext) + : 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(); + + /// + /// Checks if the umbraco context id is valid + /// + /// + /// + protected bool ValidateUserContextId(string currentUmbracoUserContextId) + { + return BasePage.ValidateUserContextID(currentUmbracoUserContextId); + } + + /// + /// Checks if the username/password credentials are valid + /// + /// + /// + /// + protected bool ValidateCredentials(string username, string password) + { + return Membership.Providers[UmbracoSettings.DefaultBackofficeProvider].ValidateUser(username, password); + } + + /// + /// Validates the user for access to a certain application + /// + /// The application alias. + /// true if an exception should be thrown if authorization fails + /// + protected bool AuthorizeRequest(string app, bool throwExceptions = false) + { + //ensure we have a valid user first! + if (!AuthorizeRequest(throwExceptions)) return false; + + //if it is empty, don't validate + if (app.IsNullOrWhiteSpace()) + { + return true; + } + var hasAccess = UserHasAppAccess(app, UmbracoUser); + if (!hasAccess && throwExceptions) + throw new UserAuthorizationException("The user does not have access to the required application"); + return hasAccess; + } + + /// + /// Checks if the specified user as access to the app + /// + /// + /// + /// + protected 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 + /// + /// + /// + /// + 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); + } + + /// + /// Returns true if there is a valid logged in user and that ssl is enabled if required + /// + /// true if an exception should be thrown if authorization fails + /// + 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; + } + } + + /// + /// Returns the current user + /// + protected User UmbracoUser + { + get + { + return _user ?? (_user = _page.getUser()); + } + } + + /// + /// 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 497ad77a6e..ac55ebecbc 100644 --- a/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs +++ b/src/Umbraco.Web/WebServices/UmbracoAuthorizedWebService.cs @@ -9,8 +9,6 @@ using umbraco.BasePages; using umbraco.BusinessLogic; using Umbraco.Core; using umbraco.businesslogic.Exceptions; -using GlobalSettings = umbraco.GlobalSettings; -using UmbracoSettings = umbraco.UmbracoSettings; namespace Umbraco.Web.WebServices { @@ -28,6 +26,9 @@ 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(); diff --git a/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs b/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs new file mode 100644 index 0000000000..28e7619b04 --- /dev/null +++ b/src/Umbraco.Web/WebServices/UmbracoHttpHandler.cs @@ -0,0 +1,74 @@ +using System; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using Umbraco.Core; + +namespace Umbraco.Web.WebServices +{ + public abstract class UmbracoHttpHandler : IHttpHandler + { + public abstract void ProcessRequest(HttpContext context); + public abstract bool IsReusable { get; } + + protected UmbracoHttpHandler() + : this(UmbracoContext.Current) + { + + } + + protected UmbracoHttpHandler(UmbracoContext umbracoContext) + { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + UmbracoContext = umbracoContext; + Umbraco = new UmbracoHelper(umbracoContext); + } + + /// + /// Returns the current ApplicationContext + /// + public ApplicationContext ApplicationContext + { + get { return UmbracoContext.Application; } + } + + /// + /// Returns the current UmbracoContext + /// + public UmbracoContext UmbracoContext { get; private set; } + + /// + /// Returns an UmbracoHelper object + /// + public UmbracoHelper Umbraco { get; private set; } + + private UrlHelper _url; + + /// + /// Returns a UrlHelper + /// + /// + /// This URL helper is created without any route data and an empty request context + /// + public UrlHelper Url + { + 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 DatabaseContext + ///// + //public DatabaseContext DatabaseContext + //{ + // get { return ApplicationContext.DatabaseContext; } + //} + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/TreeDataService.ashx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/TreeDataService.ashx.cs index 89609703aa..ebd49d7af4 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/TreeDataService.ashx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/TreeDataService.ashx.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Web; using System.Web.Services; +using Umbraco.Web.WebServices; using umbraco.cms.presentation.Trees; using System.Threading; @@ -13,19 +14,18 @@ namespace umbraco.presentation.webservices [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] - public class TreeDataService : IHttpHandler + public class TreeDataService : UmbracoAuthorizedHttpHandler { - public void ProcessRequest(HttpContext context) + public override void ProcessRequest(HttpContext context) { - Authorize(); - //Thread.Sleep(100000); + AuthorizeRequest(true); context.Response.ContentType = "application/json"; context.Response.Write(GetXmlTree().ToString()); } - public bool IsReusable + public override bool IsReusable { get { @@ -33,6 +33,7 @@ namespace umbraco.presentation.webservices } } + [Obsolete("Use the base class AuthorizeRequest methods in UmbracoAuthorizedHttpHandler")] public static void Authorize() { if (!BasePages.BasePage.ValidateUserContextID(BasePages.BasePage.umbracoUserContextID)) @@ -46,7 +47,10 @@ namespace umbraco.presentation.webservices /// public XmlTree GetXmlTree() { - TreeRequestParams treeParams = TreeRequestParams.FromQueryStrings(); + var treeParams = TreeRequestParams.FromQueryStrings(); + + //validate the current user for the request app! + AuthorizeRequest(treeParams.Application, true); if (string.IsNullOrEmpty(treeParams.TreeType)) if (!string.IsNullOrEmpty(treeParams.Application)) @@ -56,10 +60,10 @@ namespace umbraco.presentation.webservices else LoadTree(treeParams); - return xTree; + return _xTree; } - private XmlTree xTree = new XmlTree(); + private XmlTree _xTree = new XmlTree(); /// /// If the application supports multiple trees, then this function iterates over all of the trees assigned to it @@ -75,7 +79,7 @@ namespace umbraco.presentation.webservices { BaseTree bTree = treeDef.CreateInstance(); bTree.SetTreeParameters(treeParams); - xTree.Add(bTree.RootNode); + _xTree.Add(bTree.RootNode); } } @@ -92,7 +96,7 @@ namespace umbraco.presentation.webservices { BaseTree bTree = treeDef.CreateInstance(); bTree.SetTreeParameters(treeParams); - bTree.Render(ref xTree); + bTree.Render(ref _xTree); } else LoadNullTree(treeParams); @@ -105,7 +109,7 @@ namespace umbraco.presentation.webservices { BaseTree nullTree = new NullTree(treeParams.Application); nullTree.SetTreeParameters(treeParams); - nullTree.Render(ref xTree); + nullTree.Render(ref _xTree); } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UltimatePickerAutoCompleteHandler.ashx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UltimatePickerAutoCompleteHandler.ashx.cs index 97bd1934b5..8fa77e7a8b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UltimatePickerAutoCompleteHandler.ashx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UltimatePickerAutoCompleteHandler.ashx.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; +using Umbraco.Web.WebServices; +using umbraco.BusinessLogic; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic; @@ -13,19 +15,24 @@ namespace umbraco.presentation.umbraco.webservices /// [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - public class UltimatePickerAutoCompleteHandler : IHttpHandler + public class UltimatePickerAutoCompleteHandler : UmbracoAuthorizedHttpHandler { - private int nodeCount; - private int Counter; - private string[] output; - private string prefix; + private int _nodeCount; + private int _counter; + private string[] _output; + private string _prefix; - public void ProcessRequest(HttpContext context) + public override void ProcessRequest(HttpContext context) { + //user must be allowed to see content or media + if (!AuthorizeRequest(DefaultApps.content.ToString()) && !AuthorizeRequest(DefaultApps.media.ToString())) + return; + + context.Response.ContentType = "text/plain"; - prefix = context.Request.QueryString["q"]; + _prefix = context.Request.QueryString["q"]; int parentNodeId = Convert.ToInt32(context.Request.QueryString["id"]); bool showGrandChildren = Convert.ToBoolean(context.Request.QueryString["showchildren"]); @@ -37,7 +44,7 @@ namespace umbraco.presentation.umbraco.webservices CMSNode parent = new CMSNode(parentNodeId); if (!showGrandChildren) { - nodeCount = 0; + _nodeCount = 0; //store children array here because iterating over an Array property object is very inneficient. var children = parent.Children; @@ -45,42 +52,42 @@ namespace umbraco.presentation.umbraco.webservices { - nodeChildrenCount(child, false, documentAliasFilters); + NodeChildrenCount(child, false, documentAliasFilters); } - output = new string[nodeCount]; + _output = new string[_nodeCount]; - Counter = 0; + _counter = 0; int level = 1; //why is there a 2nd iteration of the same thing here? foreach (CMSNode child in children) { - addNode(child, level, showGrandChildren, documentAliasFilters); + AddNode(child, level, showGrandChildren, documentAliasFilters); } } else { - nodeCount = 0; + _nodeCount = 0; //store children array here because iterating over an Array property object is very inneficient. var children = parent.Children; foreach (CMSNode child in children) { - nodeChildrenCount(child, true, documentAliasFilters); + NodeChildrenCount(child, true, documentAliasFilters); } - output = new string[nodeCount]; - Counter = 0; + _output = new string[_nodeCount]; + _counter = 0; int level = 1; foreach (CMSNode child in children) { - addNode(child, level, showGrandChildren, documentAliasFilters); + AddNode(child, level, showGrandChildren, documentAliasFilters); } @@ -88,21 +95,21 @@ namespace umbraco.presentation.umbraco.webservices } - foreach (string item in output) + foreach (string item in _output) { context.Response.Write(item + Environment.NewLine); } } - private bool validNode(string nodeText) + private bool ValidNode(string nodeText) { - if (nodeText.Length >= prefix.Length) + if (nodeText.Length >= _prefix.Length) { - if (nodeText.Substring(0, prefix.Length).ToLower() == prefix.ToLower()) + if (nodeText.Substring(0, _prefix.Length).ToLower() == _prefix.ToLower()) { return true; } @@ -111,7 +118,7 @@ namespace umbraco.presentation.umbraco.webservices return false; } - private void nodeChildrenCount(CMSNode node, bool countChildren, string[] documentAliasFilters) + private void NodeChildrenCount(CMSNode node, bool countChildren, string[] documentAliasFilters) { if (documentAliasFilters.Length > 0) { @@ -123,9 +130,9 @@ namespace umbraco.presentation.umbraco.webservices if (new Document(node.Id).ContentType.Alias == trimmedFilter || trimmedFilter == string.Empty) { - if (validNode(node.Text)) + if (ValidNode(node.Text)) { - nodeCount += 1; + _nodeCount += 1; } } @@ -133,9 +140,9 @@ namespace umbraco.presentation.umbraco.webservices } else { - if (validNode(node.Text)) + if (ValidNode(node.Text)) { - nodeCount += 1; + _nodeCount += 1; } } @@ -145,13 +152,13 @@ namespace umbraco.presentation.umbraco.webservices var children = node.Children; foreach (CMSNode child in children) { - nodeChildrenCount(child, countChildren, documentAliasFilters); + NodeChildrenCount(child, countChildren, documentAliasFilters); } } } - private void addNode(CMSNode node, int level, bool showGrandChildren, string[] documentAliasFilters) + private void AddNode(CMSNode node, int level, bool showGrandChildren, string[] documentAliasFilters) { string preText = string.Empty; @@ -170,10 +177,10 @@ namespace umbraco.presentation.umbraco.webservices if (new Document(node.Id).ContentType.Alias == trimmedFilter || trimmedFilter == string.Empty) { - if (validNode(node.Text)) + if (ValidNode(node.Text)) { - output[Counter] = preText + node.Text + " [" + node.Id + "]"; - Counter++; + _output[_counter] = preText + node.Text + " [" + node.Id + "]"; + _counter++; } } @@ -181,10 +188,10 @@ namespace umbraco.presentation.umbraco.webservices } else { - if (validNode(node.Text)) + if (ValidNode(node.Text)) { - output[Counter] = preText + node.Text + " [" + node.Id + "]"; - Counter++; + _output[_counter] = preText + node.Text + " [" + node.Id + "]"; + _counter++; } } @@ -196,13 +203,13 @@ namespace umbraco.presentation.umbraco.webservices var children = node.Children; foreach (CMSNode child in children) { - addNode(child, level + 1, showGrandChildren, documentAliasFilters); + AddNode(child, level + 1, showGrandChildren, documentAliasFilters); } } } } - public bool IsReusable + public override bool IsReusable { get {