Fix cookie from being set to empty pathbase
Found with issue: https://github.com/aspnet/Mvc/issues/5512
This commit is contained in:
parent
968ea59ce4
commit
a5c0e505c1
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
var options = new CookieOptions();
|
var options = new CookieOptions();
|
||||||
options.HttpOnly = true;
|
options.HttpOnly = true;
|
||||||
options.Path = _options.CookiePath ?? httpContext.Request.PathBase;
|
options.Path = _options.CookiePath ?? GetPathBase(httpContext);
|
||||||
options.Domain = _options.CookieDomain;
|
options.Domain = _options.CookieDomain;
|
||||||
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
|
// Note: don't use "newCookie.Secure = _options.RequireSSL;" since the default
|
||||||
// value of newCookie.Secure is populated out of band.
|
// value of newCookie.Secure is populated out of band.
|
||||||
|
|
@ -82,5 +82,15 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
|
|
||||||
httpContext.Response.Cookies.Append(_options.CookieName, token, options);
|
httpContext.Response.Cookies.Append(_options.CookieName, token, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetPathBase(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var pathBase = httpContext.Request.PathBase.ToString();
|
||||||
|
if (string.IsNullOrEmpty(pathBase))
|
||||||
|
{
|
||||||
|
pathBase = "/";
|
||||||
|
}
|
||||||
|
return pathBase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||||
|
|
@ -26,11 +27,19 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||||
var response = await Client.GetAsync("http://localhost/Index.html");
|
var response = await Client.GetAsync("http://localhost/Index.html");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var cookie = RetrieveAntiforgeryCookie(response);
|
var setCookieHeaderValue = RetrieveAntiforgeryCookie(response);
|
||||||
Assert.NotNull(cookie.Value);
|
Assert.NotNull(setCookieHeaderValue);
|
||||||
|
Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value));
|
||||||
|
Assert.Null(setCookieHeaderValue.Domain);
|
||||||
|
Assert.Equal("/", setCookieHeaderValue.Path);
|
||||||
|
Assert.False(setCookieHeaderValue.Secure);
|
||||||
|
|
||||||
var token = RetrieveAntiforgeryToken(response);
|
setCookieHeaderValue = RetrieveAntiforgeryToken(response);
|
||||||
Assert.NotNull(token.Value);
|
Assert.NotNull(setCookieHeaderValue);
|
||||||
|
Assert.False(string.IsNullOrEmpty(setCookieHeaderValue.Value));
|
||||||
|
Assert.Null(setCookieHeaderValue.Domain);
|
||||||
|
Assert.Equal("/", setCookieHeaderValue.Path);
|
||||||
|
Assert.False(setCookieHeaderValue.Secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -49,7 +58,7 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Contains($"The required antiforgery cookie \"{cookie.Key}\" is not present.", exception.Message);
|
Assert.Contains($"The required antiforgery cookie \"{cookie.Name}\" is not present.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -63,8 +72,8 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||||
|
|
||||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
|
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/items");
|
||||||
|
|
||||||
|
httpRequestMessage.Headers.Add("Cookie", $"{cookie.Name}={cookie.Value}");
|
||||||
httpRequestMessage.Headers.Add("X-XSRF-TOKEN", token.Value);
|
httpRequestMessage.Headers.Add("X-XSRF-TOKEN", token.Value);
|
||||||
httpRequestMessage.Headers.Add("Cookie", $"{cookie.Key}={cookie.Value}");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await Client.SendAsync(httpRequestMessage);
|
var response = await Client.SendAsync(httpRequestMessage);
|
||||||
|
|
@ -73,24 +82,20 @@ namespace Microsoft.AspNetCore.Antiforgery.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static KeyValuePair<string, string> RetrieveAntiforgeryToken(HttpResponseMessage response)
|
private static SetCookieHeaderValue RetrieveAntiforgeryToken(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
return GetCookie(response, 1);
|
return response.Headers.GetValues(HeaderNames.SetCookie)
|
||||||
|
.Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue))
|
||||||
|
.Where(setCookieHeaderValue => setCookieHeaderValue.Name == "XSRF-TOKEN")
|
||||||
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static KeyValuePair<string, string> RetrieveAntiforgeryCookie(HttpResponseMessage response)
|
private static SetCookieHeaderValue RetrieveAntiforgeryCookie(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
return GetCookie(response, 0);
|
return response.Headers.GetValues(HeaderNames.SetCookie)
|
||||||
}
|
.Select(setCookieValue => SetCookieHeaderValue.Parse(setCookieValue))
|
||||||
|
.Where(setCookieHeaderValue => setCookieHeaderValue.Name.StartsWith(".AspNetCore.Antiforgery."))
|
||||||
private static KeyValuePair<string, string> GetCookie(HttpResponseMessage response, int index)
|
.FirstOrDefault();
|
||||||
{
|
|
||||||
var setCookieArray = response.Headers.GetValues("Set-Cookie").ToArray();
|
|
||||||
var cookie = setCookieArray[index].Split(';').First().Split('=');
|
|
||||||
var cookieKey = cookie[0];
|
|
||||||
var cookieData = cookie[1];
|
|
||||||
|
|
||||||
return new KeyValuePair<string, string>(cookieKey, cookieData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,10 +274,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("/")]
|
[InlineData(null, "/")]
|
||||||
[InlineData("/vdir1")]
|
[InlineData("", "/")]
|
||||||
[InlineData("/vdir1/vdir2")]
|
[InlineData("/", "/")]
|
||||||
public void SaveCookieToken_SetsCookieWithApproriatePathBase(string requestPathBase)
|
[InlineData("/vdir1", "/vdir1")]
|
||||||
|
[InlineData("/vdir1/vdir2", "/vdir1/vdir2")]
|
||||||
|
public void SaveCookieToken_SetsCookieWithApproriatePathBase(string requestPathBase, string expectedCookiePath)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var token = "serialized-value";
|
var token = "serialized-value";
|
||||||
|
|
@ -305,7 +307,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.Equal(_cookieName, cookies.Key);
|
Assert.Equal(_cookieName, cookies.Key);
|
||||||
Assert.Equal("serialized-value", cookies.Value);
|
Assert.Equal("serialized-value", cookies.Value);
|
||||||
Assert.True(cookies.Options.HttpOnly);
|
Assert.True(cookies.Options.HttpOnly);
|
||||||
Assert.Equal(requestPathBase, cookies.Options.Path);
|
Assert.Equal(expectedCookiePath, cookies.Options.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue