diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index ea510c200b..e7ecc2a8f6 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Dynamics; using Umbraco.Web.Mvc; using umbraco; using umbraco.cms.businesslogic.member; +using System.Web.Security; namespace Umbraco.Web { @@ -112,18 +113,36 @@ namespace Umbraco.Web viewContext.HttpContext.Server.UrlEncode(surfaceAction), area); - var additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); - if (!additionalRouteValsAsQuery.IsNullOrWhiteSpace()) - surfaceRouteParams = "&" + additionalRouteValsAsQuery; + //var additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); - _base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(surfaceRouteParams)); + //U4-1454 SurfaceController additionalRouteVal parameters do not handle equals sign correctly + string additionalRouteValsAsQuery = string.Empty; + var additionalRouteValsDict = additionalRouteVals.ToDictionary(); + + foreach (var item in additionalRouteValsDict) + { + additionalRouteValsAsQuery += viewContext.HttpContext.Server.UrlEncode(item.Key) + "=" + viewContext.HttpContext.Server.UrlEncode(item.Value.ToString()) + "&"; + } + + if (additionalRouteValsAsQuery.Length > 0) + additionalRouteValsAsQuery = additionalRouteValsAsQuery.Substring(0, additionalRouteValsAsQuery.Length - 1); + + if (!additionalRouteValsAsQuery.IsNullOrWhiteSpace()) + surfaceRouteParams += "&" + additionalRouteValsAsQuery; + + if (!string.IsNullOrWhiteSpace(surfaceRouteParams)) + { + //U4-1455 SurfaceController additionalRouteVal parameters should be encrypted + var bytesToEncrypt = Encoding.UTF8.GetBytes(surfaceRouteParams); + _encryptedString = MachineKey.Encode(bytesToEncrypt, MachineKeyProtection.All); + } _textWriter = viewContext.Writer; } private bool _disposed; - private readonly string _base64String; + private readonly string _encryptedString; private readonly TextWriter _textWriter; protected override void Dispose(bool disposing) @@ -133,7 +152,7 @@ namespace Umbraco.Web this._disposed = true; //write out the hidden surface form routes - _textWriter.Write(""); + _textWriter.Write(""); base.Dispose(disposing); } diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 8bf2f7796b..33368d2afc 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -10,12 +10,22 @@ using Umbraco.Core.Models; using Umbraco.Web.Models; using Umbraco.Web.Routing; using umbraco.cms.businesslogic.template; +using System.Collections.Generic; +using System.Web.Security; namespace Umbraco.Web.Mvc { public class RenderRouteHandler : IRouteHandler { - + // Define reserved dictionary keys for controller, action and area specified in route additional values data + private static string RESERVED_ADDITIONAL_KEY_CONTROLLER = "c"; + private static string RESERVED_ADDITIONAL_KEY_ACTION = "a"; + private static string RESERVED_ADDITIONAL_KEY_AREA = "ar"; + private static string[] RESERVED_ADDITIONAL_KEYS = new string[] { + RESERVED_ADDITIONAL_KEY_CONTROLLER, + RESERVED_ADDITIONAL_KEY_ACTION, + RESERVED_ADDITIONAL_KEY_AREA }; + public RenderRouteHandler(IControllerFactory controllerFactory) { if (controllerFactory == null) throw new ArgumentNullException("controllerFactory"); @@ -91,6 +101,45 @@ namespace Umbraco.Web.Mvc requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage } + /// + /// Decrypt additional route values + /// + /// + /// + public static IDictionary AdditionalRouteValues(RequestContext requestContext) + { + Dictionary values = new Dictionary(); + + if (requestContext.HttpContext.Request.RequestType != "POST") + return values; + + //this field will contain a base64 encoded version of the surface route vals + if (requestContext.HttpContext.Request["uformpostroutevals"].IsNullOrWhiteSpace()) + return values; + + var encodedVal = requestContext.HttpContext.Request["uformpostroutevals"]; + + if (!string.IsNullOrWhiteSpace(encodedVal)) + { + // Decrypt additional values + var decodedBytes = MachineKey.Decode(encodedVal, MachineKeyProtection.All); + var decodedString = Encoding.UTF8.GetString(decodedBytes); + + // Parse as query string + var decodedParts = decodedString.Split('&').Select(x => new { Key = x.Split('=')[0], Value = x.Split('=')[1] }).ToArray(); + + if (decodedParts != null) + { + foreach (var part in decodedParts) + { + values[part.Key] = HttpUtility.UrlDecode(part.Value); + } + } + } + + return values; + } + /// /// Checks the request and query strings to see if it matches the definition of having a Surface controller /// posted value, if so, then we return a PostedDataProxyInfo object with the correct information. @@ -106,22 +155,20 @@ namespace Umbraco.Web.Mvc if (requestContext.HttpContext.Request["uformpostroutevals"].IsNullOrWhiteSpace()) return null; - var encodedVal = requestContext.HttpContext.Request["uformpostroutevals"]; - var decodedString = Encoding.UTF8.GetString(Convert.FromBase64String(encodedVal)); - //the value is formatted as query strings - var decodedParts = decodedString.Split('&').Select(x => new { Key = x.Split('=')[0], Value = x.Split('=')[1] }).ToArray(); + // U4-1455 SurfaceController additionalRouteVal parameters should be encrypted + var decodedParts = AdditionalRouteValues(requestContext); //validate all required keys exist //the controller - if (!decodedParts.Any(x => x.Key == "c")) - return null; - //the action - if (!decodedParts.Any(x => x.Key == "a")) - return null; - //the area - if (!decodedParts.Any(x => x.Key == "ar")) - return null; + if (!decodedParts.Any(x => x.Key == RESERVED_ADDITIONAL_KEY_CONTROLLER)) + return null; + //the action + if (!decodedParts.Any(x => x.Key == RESERVED_ADDITIONAL_KEY_ACTION)) + return null; + //the area + if (!decodedParts.Any(x => x.Key == RESERVED_ADDITIONAL_KEY_AREA)) + return null; ////the controller type, if it contains this then it is a plugin controller, not locally declared. //if (decodedParts.Any(x => x.Key == "t")) @@ -135,12 +182,19 @@ namespace Umbraco.Web.Mvc // }; //} + //U4-1453 SurfaceController additionalRouteVal parameters not passed to controller action + foreach (var item in decodedParts.Where(x => !RESERVED_ADDITIONAL_KEYS.Contains(x.Key))) + { + // Populate route with additional values which aren't reserved values + requestContext.RouteData.Values.Add(item.Key, item.Value); + } + //return the proxy info without the surface id... could be a local controller. return new PostedDataProxyInfo { - ControllerName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "c").Value), - ActionName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "a").Value), - Area = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == "ar").Value), + ControllerName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == RESERVED_ADDITIONAL_KEY_CONTROLLER).Value), + ActionName = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == RESERVED_ADDITIONAL_KEY_ACTION).Value), + Area = requestContext.HttpContext.Server.UrlDecode(decodedParts.Single(x => x.Key == RESERVED_ADDITIONAL_KEY_AREA).Value), }; }