From d71b8a139fb692261cedffc92396ef0ec1849931 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 29 Aug 2017 10:50:20 +1000 Subject: [PATCH] Moves GetCookieValue to an extension method, cleans up code, adds unit tests --- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Web/HttpCookieExtensionsTests.cs | 44 +++++++++++++++++++ src/Umbraco.Web/HttpCookieExtensions.cs | 37 ++++++++++++++++ .../Filters/AngularAntiForgeryHelper.cs | 32 +------------- 4 files changed, 84 insertions(+), 30 deletions(-) create mode 100644 src/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ebee151364..39ad21f15c 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -212,6 +212,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/HttpCookieExtensions.cs b/src/Umbraco.Web/HttpCookieExtensions.cs index 7aec1466da..d67e1894fd 100644 --- a/src/Umbraco.Web/HttpCookieExtensions.cs +++ b/src/Umbraco.Web/HttpCookieExtensions.cs @@ -16,6 +16,43 @@ 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) + { + var cookieRequestHeader = requestHeaders + .Where(h => h.Key.Equals("Cookie", StringComparison.InvariantCultureIgnoreCase)) + .ToArray(); + + if (cookieRequestHeader.Length == 0) + return null; + + var cookiesHeader = cookieRequestHeader[0]; + + var cookiesHeaderValue = cookiesHeader.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().InvariantEquals(cookieName)) + return parts[1].Trim(); + } + + return null; + } + /// /// Removes the cookie from the request and the response if it exists /// diff --git a/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs b/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs index 0f2a7ac442..b83b4114a3 100644 --- a/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs +++ b/src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs @@ -107,41 +107,13 @@ namespace Umbraco.Web.WebApi.Filters /// public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason) { - var cookieToken = GetCookie(requestHeaders, CsrfValidationCookieName); + var cookieToken = requestHeaders.GetCookieValue(CsrfValidationCookieName); return ValidateHeaders( requestHeaders.ToDictionary(x => x.Key, x => x.Value).ToArray(), cookieToken == null ? null : cookieToken, out failedReason); } - - /// - /// Retrieves an individual cookie from the cookies collection - /// Adapted from: https://stackoverflow.com/a/29057304/5018 - /// - /// - /// - /// - private static string GetCookie(HttpRequestHeaders requestHeaders, string cookieName) - { - var cookieRequestHeader = requestHeaders.Where(h => h.Key.Equals("Cookie", StringComparison.InvariantCultureIgnoreCase)).ToArray(); - if (cookieRequestHeader.Any() == false) - return null; - - var cookiesHeader = cookieRequestHeader.FirstOrDefault(); - if (cookiesHeader.Value == null) - return null; - - var cookiesHeaderValue = cookiesHeader.Value.FirstOrDefault(); - if (cookiesHeaderValue == null) - return null; - - var cookieCollection = cookiesHeaderValue.Split(';'); - var cookiePair = cookieCollection.FirstOrDefault(c => c.Trim().StartsWith(cookieName, StringComparison.InvariantCultureIgnoreCase)); - if (cookiePair == null) - return null; - - return cookiePair.Trim().Substring(cookieName.Length + 1); - } + } } \ No newline at end of file