Skip to content

Commit 9839799

Browse files
committed
Add CookiePolicy logging #1588
1 parent 21acbf0 commit 9839799

File tree

6 files changed

+163
-15
lines changed

6 files changed

+163
-15
lines changed

samples/CookiePolicySample/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static void Main(string[] args)
1212
.ConfigureLogging(factory =>
1313
{
1414
factory.AddConsole();
15-
factory.AddFilter("Console", level => level >= LogLevel.Information);
15+
factory.AddFilter("Microsoft", LogLevel.Trace);
1616
})
1717
.UseKestrel()
1818
.UseContentRoot(Directory.GetCurrentDirectory())

src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,42 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Threading.Tasks;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Http;
78
using Microsoft.AspNetCore.Http.Features;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.Extensions.Logging.Abstractions;
811
using Microsoft.Extensions.Options;
912

1013
namespace Microsoft.AspNetCore.CookiePolicy
1114
{
1215
public class CookiePolicyMiddleware
1316
{
1417
private readonly RequestDelegate _next;
18+
private readonly ILogger _logger;
1519

16-
public CookiePolicyMiddleware(
17-
RequestDelegate next,
18-
IOptions<CookiePolicyOptions> options)
20+
public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options, ILoggerFactory factory)
21+
{
22+
Options = options.Value;
23+
_next = next ?? throw new ArgumentNullException(nameof(next));
24+
_logger = factory.CreateLogger<CookiePolicyMiddleware>();
25+
}
26+
27+
public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options)
1928
{
2029
Options = options.Value;
2130
_next = next;
31+
_logger = NullLogger.Instance;
2232
}
2333

2434
public CookiePolicyOptions Options { get; set; }
2535

2636
public Task Invoke(HttpContext context)
2737
{
2838
var feature = context.Features.Get<IResponseCookiesFeature>() ?? new ResponseCookiesFeature(context.Features);
29-
var wrapper = new ResponseCookiesWrapper(context, Options, feature);
39+
var wrapper = new ResponseCookiesWrapper(context, Options, feature, _logger);
3040
context.Features.Set<IResponseCookiesFeature>(new CookiesWrapperFeature(wrapper));
3141
context.Features.Set<ITrackingConsentFeature>(wrapper);
3242

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.Extensions.Logging
7+
{
8+
internal static class LoggingExtensions
9+
{
10+
private static Action<ILogger, bool, Exception> _needsConsent;
11+
private static Action<ILogger, bool, Exception> _hasConsent;
12+
private static Action<ILogger, Exception> _consentGranted;
13+
private static Action<ILogger, Exception> _consentWithdrawn;
14+
private static Action<ILogger, string, Exception> _cookieSuppressed;
15+
private static Action<ILogger, string, Exception> _deleteCookieSuppressed;
16+
private static Action<ILogger, string, Exception> _upgradedToSecure;
17+
private static Action<ILogger, string, string, Exception> _upgradedSameSite;
18+
private static Action<ILogger, string, Exception> _upgradedToHttpOnly;
19+
20+
static LoggingExtensions()
21+
{
22+
_needsConsent = LoggerMessage.Define<bool>(
23+
eventId: 1,
24+
logLevel: LogLevel.Trace,
25+
formatString: "Needs consent: {needsConsent}.");
26+
_hasConsent = LoggerMessage.Define<bool>(
27+
eventId: 2,
28+
logLevel: LogLevel.Trace,
29+
formatString: "Has consent: {hasConsent}.");
30+
_consentGranted = LoggerMessage.Define(
31+
eventId: 3,
32+
logLevel: LogLevel.Debug,
33+
formatString: "Consent granted.");
34+
_consentWithdrawn = LoggerMessage.Define(
35+
eventId: 4,
36+
logLevel: LogLevel.Debug,
37+
formatString: "Consent withdrawn.");
38+
_cookieSuppressed = LoggerMessage.Define<string>(
39+
eventId: 5,
40+
logLevel: LogLevel.Debug,
41+
formatString: "Cookie '{key}' suppressed due to consent policy.");
42+
_deleteCookieSuppressed = LoggerMessage.Define<string>(
43+
eventId: 6,
44+
logLevel: LogLevel.Debug,
45+
formatString: "Delete cookie '{key}' suppressed due to developer policy.");
46+
_upgradedToSecure = LoggerMessage.Define<string>(
47+
eventId: 7,
48+
logLevel: LogLevel.Debug,
49+
formatString: "Cookie '{key}' upgraded to 'secure'.");
50+
_upgradedSameSite = LoggerMessage.Define<string, string>(
51+
eventId: 8,
52+
logLevel: LogLevel.Debug,
53+
formatString: "Cookie '{key}' same site mode upgraded to '{mode}'.");
54+
_upgradedToHttpOnly = LoggerMessage.Define<string>(
55+
eventId: 9,
56+
logLevel: LogLevel.Debug,
57+
formatString: "Cookie '{key}' upgraded to 'httponly'.");
58+
}
59+
60+
public static void NeedsConsent(this ILogger logger, bool needsConsent)
61+
{
62+
_needsConsent(logger, needsConsent, null);
63+
}
64+
65+
public static void HasConsent(this ILogger logger, bool hasConsent)
66+
{
67+
_hasConsent(logger, hasConsent, null);
68+
}
69+
70+
public static void ConsentGranted(this ILogger logger)
71+
{
72+
_consentGranted(logger, null);
73+
}
74+
75+
public static void ConsentWithdrawn(this ILogger logger)
76+
{
77+
_consentWithdrawn(logger, null);
78+
}
79+
80+
public static void CookieSuppressed(this ILogger logger, string key)
81+
{
82+
_cookieSuppressed(logger, key, null);
83+
}
84+
85+
public static void DeleteCookieSuppressed(this ILogger logger, string key)
86+
{
87+
_deleteCookieSuppressed(logger, key, null);
88+
}
89+
90+
public static void CookieUpgradedToSecure(this ILogger logger, string key)
91+
{
92+
_upgradedToSecure(logger, key, null);
93+
}
94+
95+
public static void CookieSameSiteUpgraded(this ILogger logger, string key, string mode)
96+
{
97+
_upgradedSameSite(logger, key, mode, null);
98+
}
99+
100+
public static void CookieUpgradedToHttpOnly(this ILogger logger, string key)
101+
{
102+
_upgradedToHttpOnly(logger, key, null);
103+
}
104+
}
105+
}

src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
1314
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
1415
</ItemGroup>
1516

src/Microsoft.AspNetCore.CookiePolicy/ResponseCookiesWrapper.cs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@
55
using Microsoft.AspNetCore.Builder;
66
using Microsoft.AspNetCore.Http;
77
using Microsoft.AspNetCore.Http.Features;
8+
using Microsoft.Extensions.Logging;
89

910
namespace Microsoft.AspNetCore.CookiePolicy
1011
{
1112
internal class ResponseCookiesWrapper : IResponseCookies, ITrackingConsentFeature
1213
{
1314
private const string ConsentValue = "yes";
14-
15+
private readonly ILogger _logger;
1516
private bool? _isConsentNeeded;
1617
private bool? _hasConsent;
1718

18-
public ResponseCookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature)
19+
public ResponseCookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature, ILogger logger)
1920
{
2021
Context = context;
2122
Feature = feature;
2223
Options = options;
24+
_logger = logger;
2325
}
2426

2527
private HttpContext Context { get; }
@@ -38,6 +40,7 @@ public bool IsConsentNeeded
3840
{
3941
_isConsentNeeded = Options.CheckConsentNeeded == null ? false
4042
: Options.CheckConsentNeeded(Context);
43+
_logger.NeedsConsent(_isConsentNeeded.Value);
4144
}
4245

4346
return _isConsentNeeded.Value;
@@ -52,6 +55,7 @@ public bool HasConsent
5255
{
5356
var cookie = Context.Request.Cookies[Options.ConsentCookie.Name];
5457
_hasConsent = string.Equals(cookie, ConsentValue, StringComparison.Ordinal);
58+
_logger.HasConsent(_hasConsent.Value);
5559
}
5660

5761
return _hasConsent.Value;
@@ -67,6 +71,7 @@ public void GrantConsent()
6771
var cookieOptions = Options.ConsentCookie.Build(Context);
6872
// Note policy will be applied. We don't want to bypass policy because we want HttpOnly, Secure, etc. to apply.
6973
Append(Options.ConsentCookie.Name, ConsentValue, cookieOptions);
74+
_logger.ConsentGranted();
7075
}
7176
_hasConsent = true;
7277
}
@@ -78,6 +83,7 @@ public void WithdrawConsent()
7883
var cookieOptions = Options.ConsentCookie.Build(Context);
7984
// Note policy will be applied. We don't want to bypass policy because we want HttpOnly, Secure, etc. to apply.
8085
Delete(Options.ConsentCookie.Name, cookieOptions);
86+
_logger.ConsentWithdrawn();
8187
}
8288
_hasConsent = false;
8389
}
@@ -137,12 +143,16 @@ public void Append(string key, string value, CookieOptions options)
137143
{
138144
Cookies.Append(key, value, options);
139145
}
146+
else
147+
{
148+
_logger.CookieSuppressed(key);
149+
}
140150
}
141151

142152
private bool ApplyAppendPolicy(ref string key, ref string value, CookieOptions options)
143153
{
144154
var issueCookie = CanTrack || options.IsEssential;
145-
ApplyPolicy(options);
155+
ApplyPolicy(key, options);
146156
if (Options.OnAppendCookie != null)
147157
{
148158
var context = new AppendCookieContext(Context, options, key, value)
@@ -182,7 +192,7 @@ public void Delete(string key, CookieOptions options)
182192

183193
// Assume you can always delete cookies unless directly overridden in the user event.
184194
var issueCookie = true;
185-
ApplyPolicy(options);
195+
ApplyPolicy(key, options);
186196
if (Options.OnDeleteCookie != null)
187197
{
188198
var context = new DeleteCookieContext(Context, options, key)
@@ -201,17 +211,30 @@ public void Delete(string key, CookieOptions options)
201211
{
202212
Cookies.Delete(key, options);
203213
}
214+
else
215+
{
216+
_logger.DeleteCookieSuppressed(key);
217+
}
204218
}
205219

206-
private void ApplyPolicy(CookieOptions options)
220+
private void ApplyPolicy(string key, CookieOptions options)
207221
{
208222
switch (Options.Secure)
209223
{
210224
case CookieSecurePolicy.Always:
211-
options.Secure = true;
225+
if (!options.Secure)
226+
{
227+
options.Secure = true;
228+
_logger.CookieUpgradedToSecure(key);
229+
}
212230
break;
213231
case CookieSecurePolicy.SameAsRequest:
214-
options.Secure = Context.Request.IsHttps;
232+
// Never downgrade a cookie
233+
if (Context.Request.IsHttps && !options.Secure)
234+
{
235+
options.Secure = true;
236+
_logger.CookieUpgradedToSecure(key);
237+
}
215238
break;
216239
case CookieSecurePolicy.None:
217240
break;
@@ -226,18 +249,27 @@ private void ApplyPolicy(CookieOptions options)
226249
if (options.SameSite == SameSiteMode.None)
227250
{
228251
options.SameSite = SameSiteMode.Lax;
252+
_logger.CookieSameSiteUpgraded(key, "lax");
229253
}
230254
break;
231255
case SameSiteMode.Strict:
232-
options.SameSite = SameSiteMode.Strict;
256+
if (options.SameSite != SameSiteMode.Strict)
257+
{
258+
options.SameSite = SameSiteMode.Strict;
259+
_logger.CookieSameSiteUpgraded(key, "strict");
260+
}
233261
break;
234262
default:
235263
throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Options.MinimumSameSitePolicy.ToString()}");
236264
}
237265
switch (Options.HttpOnly)
238266
{
239267
case HttpOnlyPolicy.Always:
240-
options.HttpOnly = true;
268+
if (!options.HttpOnly)
269+
{
270+
options.HttpOnly = true;
271+
_logger.CookieUpgradedToHttpOnly(key);
272+
}
241273
break;
242274
case HttpOnlyPolicy.None:
243275
break;

test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ await RunTest("/secureSame",
102102
Assert.Equal("A=A; path=/; samesite=lax", transaction.SetCookie[0]);
103103
Assert.Equal("B=B; path=/; samesite=lax", transaction.SetCookie[1]);
104104
Assert.Equal("C=C; path=/; samesite=lax", transaction.SetCookie[2]);
105-
Assert.Equal("D=D; path=/; samesite=lax", transaction.SetCookie[3]);
105+
Assert.Equal("D=D; path=/; secure; samesite=lax", transaction.SetCookie[3]);
106106
}),
107107
new RequestTest("https://example.com/secureSame",
108108
transaction =>

0 commit comments

Comments
 (0)