diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 4874f8a74a..6e638eef20 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -250,6 +250,7 @@
+
diff --git a/src/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs b/src/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs
new file mode 100644
index 0000000000..f13b94a669
--- /dev/null
+++ b/src/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Web;
+
+namespace Umbraco.Tests.Web
+{
+ [TestFixture]
+ public class HttpCookieExtensionsTests
+ {
+ [TestCase("hello=world;cookies=are fun;", "hello", "world", true)]
+ [TestCase("HELlo=world;cookies=are fun", "hello", "world", true)]
+ [TestCase("HELlo= world;cookies=are fun", "hello", "world", true)]
+ [TestCase("HELlo =world;cookies=are fun", "hello", "world", true)]
+ [TestCase("hello = world;cookies=are fun;", "hello", "world", true)]
+ [TestCase("hellos=world;cookies=are fun", "hello", "world", false)]
+ [TestCase("hello=world;cookies?=are fun?", "hello", "world", true)]
+ [TestCase("hel?lo=world;cookies=are fun?", "hel?lo", "world", true)]
+ public void Get_Cookie_Value_From_HttpRequestHeaders(string cookieHeaderVal, string cookieName, string cookieVal, bool matches)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
+ var requestHeaders = request.Headers;
+ requestHeaders.Add("Cookie", cookieHeaderVal);
+
+ var valueFromHeader = requestHeaders.GetCookieValue(cookieName);
+
+ if (matches)
+ {
+ Assert.IsNotNull(valueFromHeader);
+ Assert.AreEqual(cookieVal, valueFromHeader);
+ }
+ else
+ {
+ Assert.IsNull(valueFromHeader);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js
index 6f5712a78d..b283a1fec8 100644
--- a/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js
+++ b/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js
@@ -120,6 +120,8 @@ angular.module('umbraco.security.interceptor')
// We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
.config(['$httpProvider', function ($httpProvider) {
+ $httpProvider.defaults.xsrfHeaderName = 'X-UMB-XSRF-TOKEN';
+ $httpProvider.defaults.xsrfCookieName = 'UMB-XSRF-TOKEN';
$httpProvider.responseInterceptors.push('securityInterceptor');
$httpProvider.interceptors.push('umbracoRequestInterceptor');
}]);
diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js
index 5c70ca6a90..631e38657b 100644
--- a/src/Umbraco.Web.UI.Client/src/init.js
+++ b/src/Umbraco.Web.UI.Client/src/init.js
@@ -7,7 +7,7 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
// it cannot be static
$.ajaxSetup({
beforeSend: function (xhr) {
- xhr.setRequestHeader("X-XSRF-TOKEN", $cookies["XSRF-TOKEN"]);
+ xhr.setRequestHeader("X-UMB-XSRF-TOKEN", $cookies["UMB-XSRF-TOKEN"]);
if (queryStrings.getParams().umbDebug === "true" || queryStrings.getParams().umbdebug === "true") {
xhr.setRequestHeader("X-UMB-DEBUG", "true");
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js
index 05d1e5fbfa..60d762738f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.controller.js
@@ -1,4 +1,15 @@
-function textAreaController($rootScope, $scope, $log) {
+function textAreaController($scope) {
+
+ // macro parameter editor doesn't contains a config object,
+ // so we create a new one to hold any properties
+ if (!$scope.model.config) {
+ $scope.model.config = {};
+ }
+
+ if (!$scope.model.config.maxChars) {
+ $scope.model.config.maxChars = false;
+ }
+
$scope.model.maxlength = false;
if ($scope.model.config && $scope.model.config.maxChars) {
$scope.model.maxlength = true;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js
index c0f084f0c3..3bb909c717 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textbox/textbox.controller.js
@@ -1,4 +1,15 @@
-function textboxController($rootScope, $scope, $log) {
+function textboxController($scope) {
+
+ // macro parameter editor doesn't contains a config object,
+ // so we create a new one to hold any properties
+ if (!$scope.model.config) {
+ $scope.model.config = {};
+ }
+
+ if (!$scope.model.config.maxChars) {
+ $scope.model.config.maxChars = false;
+ }
+
$scope.model.maxlength = false;
if ($scope.model.config && $scope.model.config.maxChars) {
$scope.model.maxlength = true;
diff --git a/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js b/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js
index 9884df1c80..e7af064229 100644
--- a/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js
+++ b/src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js
@@ -26,7 +26,7 @@
}
};
}
-
+
if (!window.location.getParams) {
var pl = /\+/g; // Regex for replacing addition symbol with a space
var search = /([^&=]+)=?([^&]*)/g;
@@ -38,11 +38,11 @@
var urlParams = {};
while (match = search.exec(query))
- urlParams[decode(match[1])] = decode(match[2]);
-
+ urlParams[decode(match[1])] = decode(match[2]);
+
return urlParams;
}
- }
+ }
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (str) {
@@ -388,12 +388,12 @@
return null;
}
- var cookieVal = getCookie("XSRF-TOKEN");
+ var cookieVal = getCookie("UMB-XSRF-TOKEN");
if (cookieVal) {
- xhr.setRequestHeader("X-XSRF-TOKEN", cookieVal);
+ xhr.setRequestHeader("X-UMB-XSRF-TOKEN", cookieVal);
}
- var queryString = window.location.getParams();
+ var queryString = window.location.getParams();
if (queryString.umbDebug === "true") {
xhr.setRequestHeader("X-UMB-DEBUG", cookieVal);
}
diff --git a/src/Umbraco.Web/HttpCookieExtensions.cs b/src/Umbraco.Web/HttpCookieExtensions.cs
index 7aec1466da..d2133927a0 100644
--- a/src/Umbraco.Web/HttpCookieExtensions.cs
+++ b/src/Umbraco.Web/HttpCookieExtensions.cs
@@ -16,6 +16,40 @@ namespace Umbraco.Web
///
internal static class HttpCookieExtensions
{
+ ///
+ /// Retrieves an individual cookie from the cookies collection
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Adapted from: https://stackoverflow.com/a/29057304/5018 because there's an issue with .NET WebApi cookie parsing logic
+ /// when using requestHeaders.GetCookies() when an invalid cookie name is present.
+ ///
+ public static string GetCookieValue(this HttpRequestHeaders requestHeaders, string cookieName)
+ {
+ foreach (var header in requestHeaders)
+ {
+ if (header.Key.Equals("Cookie", StringComparison.InvariantCultureIgnoreCase) == false)
+ continue;
+
+ var cookiesHeaderValue = header.Value.FirstOrDefault();
+ if (cookiesHeaderValue == null)
+ return null;
+
+ var cookieCollection = cookiesHeaderValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var cookieNameValue in cookieCollection)
+ {
+ var parts = cookieNameValue.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length != 2) continue;
+ if (parts[0].Trim().Equals(cookieName, StringComparison.InvariantCultureIgnoreCase))
+ return parts[1].Trim();
+ }
+ }
+
+ return null;
+ }
+
///
/// Removes the cookie from the request and the response if it exists
///
diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
index c11252d2ca..5773d88f73 100644
--- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
+++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
@@ -37,12 +37,7 @@ namespace Umbraco.Web
protected virtual void ConfigureServices(IAppBuilder app)
{
app.SetUmbracoLoggerFactory();
-
- //Configure the Identity user manager for use with Umbraco Back office
- // (EXPERT: an overload accepts a custom BackOfficeUserStore implementation)
- app.ConfigureUserManagerForUmbracoBackOffice(
- ApplicationContext,
- Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
+ ConfigureUmbracoUserManager(app);
}
///
@@ -62,7 +57,19 @@ namespace Umbraco.Web
}
///
- /// Raised when the middelware has been configured
+ /// Configure the Identity user manager for use with Umbraco Back office
+ ///
+ ///
+ protected virtual void ConfigureUmbracoUserManager(IAppBuilder app)
+ {
+ // (EXPERT: an overload accepts a custom BackOfficeUserStore implementation)
+ app.ConfigureUserManagerForUmbracoBackOffice(
+ ApplicationContext,
+ Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
+ }
+
+ ///
+ /// Raised when the middleware has been configured
///
public static event EventHandler MiddlewareConfigured;
diff --git a/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs b/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs
index 962183f7ef..dfeeee536d 100644
--- a/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs
+++ b/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Linq;
-using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Helpers;
using Umbraco.Core;
@@ -18,19 +16,17 @@ namespace Umbraco.Web.WebApi.Filters
///
/// The cookie name that is used to store the validation value
///
- public const string CsrfValidationCookieName = "XSRF-V";
+ public const string CsrfValidationCookieName = "UMB-XSRF-V";
///
- /// The cookie name that is set for angular to use to pass in to the header value for "X-XSRF-TOKEN"
+ /// The cookie name that is set for angular to use to pass in to the header value for "X-UMB-XSRF-TOKEN"
///
- public const string AngularCookieName = "XSRF-TOKEN";
+ public const string AngularCookieName = "UMB-XSRF-TOKEN";
///
/// The header name that angular uses to pass in the token to validate the cookie
///
- public const string AngularHeadername = "X-XSRF-TOKEN";
-
-
+ public const string AngularHeadername = "X-UMB-XSRF-TOKEN";
///
/// Returns 2 tokens - one for the cookie value and one that angular should set as the header value
@@ -68,8 +64,8 @@ namespace Umbraco.Web.WebApi.Filters
return true;
}
- internal static bool ValidateHeaders(
- KeyValuePair>[] requestHeaders,
+ internal static bool ValidateHeaders(
+ KeyValuePair>[] requestHeaders,
string cookieToken,
out string failedReason)
{
@@ -86,7 +82,7 @@ namespace Umbraco.Web.WebApi.Filters
.Select(z => z.Value)
.SelectMany(z => z)
.FirstOrDefault();
-
+
// both header and cookie must be there
if (cookieToken == null || headerToken == null)
{
@@ -111,15 +107,13 @@ namespace Umbraco.Web.WebApi.Filters
///
public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason)
{
- var cookieToken = requestHeaders
- .GetCookies()
- .Select(c => c[CsrfValidationCookieName])
- .FirstOrDefault();
+ var cookieToken = requestHeaders.GetCookieValue(CsrfValidationCookieName);
return ValidateHeaders(
requestHeaders.ToDictionary(x => x.Key, x => x.Value).ToArray(),
- cookieToken == null ? null : cookieToken.Value,
+ cookieToken == null ? null : cookieToken,
out failedReason);
}
+
}
}
\ No newline at end of file