Marked CookieTempDataProvider's cookie as non-essential

[Fixes #7160] Determine if the temp data cookie is considered essential
This commit is contained in:
Kiran Challa 2018-01-05 10:47:51 -08:00
parent 1fa1d7df3e
commit 9fd60ff864
6 changed files with 162 additions and 1 deletions

View File

@ -12,6 +12,7 @@
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreCookiePolicyPackageVersion>
<MicrosoftAspNetCoreCorsPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreCorsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview1-27965</MicrosoftAspNetCoreDiagnosticsPackageVersion>

View File

@ -18,6 +18,11 @@ namespace Microsoft.AspNetCore.Mvc
HttpOnly = true,
SameSite = SameSiteMode.Strict,
// This cookie has been marked as non-essential because a user could use the SessionStateTempDataProvider,
// which is more common in production scenarios. Check the comment on CookieBuilder below
// for more information.
IsEssential = false,
// Some browsers do not allow non-secure endpoints to set cookies with a 'secure' flag or overwrite cookies
// whose 'secure' flag is set (http://httpwg.org/http-extensions/draft-ietf-httpbis-cookie-alone.html).
// Since mixing secure and non-secure endpoints is a common scenario in applications, we are relaxing the
@ -33,7 +38,11 @@ namespace Microsoft.AspNetCore.Mvc
/// <para>
/// <see cref="CookieBuilder.SameSite"/> defaults to <see cref="SameSiteMode.Strict"/>.
/// <see cref="CookieBuilder.SecurePolicy"/> defaults to <see cref="CookieSecurePolicy.SameAsRequest" />.
/// <see cref="CookieBuilder.HttpOnly"/> defaults to <c>true</c>
/// <see cref="CookieBuilder.HttpOnly"/> defaults to <c>true</c>.
/// <see cref="CookieBuilder.IsEssential"/> defaults to <c>false</c>, This property is only considered when a
/// user opts into the CookiePolicyMiddleware. If you are using this middleware and want to use
/// <see cref="CookieTempDataProvider"/>, then either set this property to <c>true</c> or
/// request user consent for non-essential cookies.
/// </para>
/// </summary>
public CookieBuilder Cookie

View File

@ -0,0 +1,112 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class TempDataInCookiesUsingCookieConsentTest
: IClassFixture<MvcTestFixture<BasicWebSite.StartupWithCookieTempDataProviderAndCookieConsent>>
{
private readonly HttpClient _client;
public TempDataInCookiesUsingCookieConsentTest(
MvcTestFixture<BasicWebSite.StartupWithCookieTempDataProviderAndCookieConsent> fixture)
{
_client = fixture.Client;
}
[Fact]
public async Task CookieTempDataProviderCookie_SetInResponse_OnGrantingConsent()
{
// Arrange
var nameValueCollection = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("value", "Foo"),
};
var content = new FormUrlEncodedContent(nameValueCollection);
// This response would have the consent cookie which would be sent on rest of the requests here
var response = await _client.GetAsync("/TempData/GrantConsent");
// Act 1
response = await _client.SendAsync(GetPostRequest("/TempData/SetTempData", content, response));
// Assert 1
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
// Act 2
response = await _client.SendAsync(GetRequest("/TempData/GetTempData", response));
// Assert 2
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("Foo", body);
// Act 3
response = await _client.SendAsync(GetRequest("/TempData/GetTempData", response));
// Assert 3
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
[Fact]
public async Task CookieTempDataProviderCookie_NotSetInResponse_OnNoConsent()
{
// Arrange
var nameValueCollection = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("value", "Foo"),
};
var content = new FormUrlEncodedContent(nameValueCollection);
// Act 1
var response = await _client.PostAsync("/TempData/SetTempData", content);
// Assert 1
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
// Act 2
response = await _client.SendAsync(GetRequest("/TempData/GetTempData", response));
// Assert 2
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
private HttpRequestMessage GetRequest(string path, HttpResponseMessage response)
{
var request = new HttpRequestMessage(HttpMethod.Get, path);
SetCookieHeaders(request, response);
return request;
}
private HttpRequestMessage GetPostRequest(string path, HttpContent content, HttpResponseMessage response)
{
var request = new HttpRequestMessage(HttpMethod.Post, path);
request.Content = content;
SetCookieHeaders(request, response);
return request;
}
private void SetCookieHeaders(HttpRequestMessage request, HttpResponseMessage response)
{
IEnumerable<string> values;
if (response.Headers.TryGetValues("Set-Cookie", out values))
{
foreach (var cookie in SetCookieHeaderValue.ParseList(values.ToList()))
{
if (cookie.Expires == null || cookie.Expires >= DateTimeOffset.UtcNow)
{
request.Headers.Add("Cookie", new CookieHeaderValue(cookie.Name, cookie.Value).ToString());
}
}
}
}
}
}

View File

@ -13,5 +13,6 @@
<PackageReference Include="Microsoft.AspNetCore.Session" Version="$(MicrosoftAspNetCoreSessionPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(MicrosoftAspNetCoreDiagnosticsPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.CookiePolicy" Version="$(MicrosoftAspNetCoreCookiePolicyPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using BasicWebSite.Filters;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
namespace BasicWebSite.Controllers
@ -117,5 +118,11 @@ namespace BasicWebSite.Controllers
{
return TempData[nameof(UnhandledExceptionAndSettingTempData)]?.ToString();
}
[HttpGet]
public void GrantConsent()
{
HttpContext.Features.Get<ITrackingConsentFeature>().GrantConsent();
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace BasicWebSite
{
public class StartupWithCookieTempDataProviderAndCookieConsent
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<CookiePolicyOptions>(o =>
{
o.CheckConsentNeeded = httpContext => true;
});
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
app.UseMvcWithDefaultRoute();
}
}
}