Skip to content

Commit ca0751f

Browse files
authored
[Cookie Compliance] Stabilize the pipeline (#8256)
* Revert "Revert "[Cookie Compliance] Revert revert and fix the functional test (#8236)"" This reverts commit 7e70a21. * Expire GA's cookie in one year * Revert "Expire GA's cookie in one year" This reverts commit ddf978f. Expire GA's cookie in another PR
1 parent 2574b00 commit ca0751f

33 files changed

Lines changed: 699 additions & 332 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
using Microsoft.Extensions.Logging;
6+
7+
namespace NuGetGallery.Cookies
8+
{
9+
public static class CookieComplianceService
10+
{
11+
public static ICookieComplianceService Instance;
12+
public static ILogger Logger;
13+
14+
public static void Initialize(ICookieComplianceService cookieComplianceService, ILogger logger)
15+
{
16+
Instance = cookieComplianceService ?? throw new ArgumentNullException(nameof(cookieComplianceService));
17+
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
18+
}
19+
}
20+
}

src/NuGetGallery.Core/Cookies/CookieComplianceServiceBase.cs

Lines changed: 0 additions & 51 deletions
This file was deleted.

src/NuGetGallery.Core/Cookies/CookieConsentMessage.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.
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+
using System.Web;
6+
using System.Linq;
7+
using System.Collections.Generic;
8+
9+
namespace NuGetGallery.Cookies
10+
{
11+
public class CookieExpirationService : ICookieExpirationService
12+
{
13+
private static readonly DateTime CookieExpirationTime = new DateTime(2010, 1, 1);
14+
15+
// Google Analytics cookies
16+
private static readonly IReadOnlyList<string> GoogleAnalyticsCookies = new[]
17+
{
18+
"_ga",
19+
"_gid",
20+
"_gat",
21+
};
22+
23+
// Application Insights cookies
24+
private static readonly IReadOnlyList<string> ApplicationInsightsCookies = new[]
25+
{
26+
"ai_user",
27+
"ai_session",
28+
};
29+
30+
private readonly string _domain;
31+
private readonly string _rootDomain;
32+
33+
public CookieExpirationService(string domain)
34+
{
35+
if (string.IsNullOrEmpty(domain))
36+
{
37+
throw new ArgumentException(CoreStrings.ArgumentCannotBeNullOrEmpty, nameof(domain));
38+
}
39+
40+
_domain = domain;
41+
_rootDomain = GetRootDomain(_domain);
42+
}
43+
44+
public void ExpireAnalyticsCookies(HttpContextBase httpContext)
45+
{
46+
if (httpContext == null)
47+
{
48+
throw new ArgumentNullException(nameof(httpContext));
49+
}
50+
51+
GoogleAnalyticsCookies.ToList().ForEach(cookieName => ExpireCookieByName(httpContext, cookieName, _rootDomain));
52+
ApplicationInsightsCookies.ToList().ForEach(cookieName => ExpireCookieByName(httpContext, cookieName));
53+
}
54+
55+
public void ExpireSocialMediaCookies(HttpContextBase httpContext) { }
56+
public void ExpireAdvertisingCookies(HttpContextBase httpContext) { }
57+
58+
public void ExpireCookieByName(HttpContextBase httpContext, string cookieName, string domain = null)
59+
{
60+
if (httpContext == null)
61+
{
62+
throw new ArgumentNullException(nameof(httpContext));
63+
}
64+
65+
if (string.IsNullOrEmpty(cookieName))
66+
{
67+
throw new ArgumentException(CoreStrings.ArgumentCannotBeNullOrEmpty, nameof(cookieName));
68+
}
69+
70+
var request = httpContext.Request;
71+
var response = httpContext.Response;
72+
if (request == null || response == null || request.Cookies == null || response.Cookies == null)
73+
{
74+
return;
75+
}
76+
77+
if (request.Cookies[cookieName] != null)
78+
{
79+
response.Cookies[cookieName].Expires = CookieExpirationTime;
80+
81+
if (domain != null)
82+
{
83+
response.Cookies[cookieName].Domain = domain;
84+
}
85+
}
86+
}
87+
88+
private string GetRootDomain(string domain)
89+
{
90+
var index1 = domain.LastIndexOf('.');
91+
if (index1 < 0)
92+
{
93+
return domain;
94+
}
95+
96+
var index2 = domain.LastIndexOf('.', index1 - 1);
97+
if (index2 < 0)
98+
{
99+
return domain;
100+
}
101+
102+
return domain.Substring(index2 + 1);
103+
}
104+
}
105+
}
Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
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.Collections.Generic;
54
using System.Web;
5+
using System.Threading.Tasks;
66

77
namespace NuGetGallery.Cookies
88
{
@@ -12,40 +12,21 @@ namespace NuGetGallery.Cookies
1212
public interface ICookieComplianceService
1313
{
1414
/// <summary>
15-
/// Determine if consent is still needed for writing non-essential cookies.
15+
/// Determine whether it's allowed to write analytics cookies.
1616
/// </summary>
17-
/// <returns>True if consent is needed, false if consent is already provided or not required.</returns>
18-
bool NeedsConsentForNonEssentialCookies(HttpRequestBase request);
17+
/// <returns>True if it's allowed.</returns>
18+
Task<bool> CanWriteAnalyticsCookiesAsync(HttpRequestBase request);
1919

2020
/// <summary>
21-
/// Determine if non-essential cookies can be written.
21+
/// Determine whether it's allowed to write social media cookies.
2222
/// </summary>
23-
/// <returns>True if non-essential cookies can be written, false otherwise.</returns>
24-
bool CanWriteNonEssentialCookies(HttpRequestBase request);
23+
/// <returns>True if it's allowed</returns>
24+
Task<bool> CanWriteSocialMediaCookiesAsync(HttpRequestBase request);
2525

2626
/// <summary>
27-
/// Get the cookie consent banner message and resources. This API is an alternative to the default
28-
/// rendering APIs below and can be used to customize the UI. Note that the messaging must remain intact.
27+
/// Determine whether it's allowed to write advertising cookies.
2928
/// </summary>
30-
CookieConsentMessage GetConsentMessage(HttpRequestBase request, string locale = null);
31-
32-
#region Default CookieConsent rendering
33-
34-
/// <summary>
35-
/// Get the default HTML markup for the cookie consent banner.
36-
/// </summary>
37-
string GetConsentMarkup(HttpRequestBase request, string locale = null);
38-
39-
/// <summary>
40-
/// Get the default CSS links for the cookie consent banner.
41-
/// </summary>
42-
IEnumerable<string> GetConsentStylesheets(HttpRequestBase request, string locale = null);
43-
44-
/// <summary>
45-
/// Get the default Javascript links for the cookie consent banner.
46-
/// </summary>
47-
IEnumerable<string> GetConsentScripts(HttpRequestBase request, string locale = null);
48-
49-
#endregion
29+
/// <returns>True if it's allowed.</returns>
30+
Task<bool> CanWriteAdvertisingCookiesAsync(HttpRequestBase request);
5031
}
51-
}
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.Web;
5+
6+
namespace NuGetGallery.Cookies
7+
{
8+
/// <summary>
9+
/// Cookie expiration service, used to expire cookies.
10+
/// </summary>
11+
public interface ICookieExpirationService
12+
{
13+
/// <summary>
14+
/// The function is used to expire analytics cookies.
15+
/// </summary>
16+
/// <param name="httpContext">The httpContext.</param>
17+
void ExpireAnalyticsCookies(HttpContextBase httpContext);
18+
19+
/// <summary>
20+
/// The function is used to expire social media cookies.
21+
/// </summary>
22+
/// <param name="httpContext">The httpContext.</param>
23+
void ExpireSocialMediaCookies(HttpContextBase httpContext);
24+
25+
/// <summary>
26+
/// The function is used to expire advertising cookies.
27+
/// </summary>
28+
/// <param name="httpContext">The httpContext.</param>
29+
void ExpireAdvertisingCookies(HttpContextBase httpContext);
30+
}
31+
}
Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,20 @@
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.Collections.Generic;
54
using System.Web;
5+
using System.Threading.Tasks;
66

77
namespace NuGetGallery.Cookies
88
{
99
/// <summary>
1010
/// Default, no-op instance of the cookie compliance service, used when no shim is registered.
1111
/// </summary>
12-
public class NullCookieComplianceService: CookieComplianceServiceBase
12+
public class NullCookieComplianceService : ICookieComplianceService
1313
{
14-
private static readonly string[] EmptyStringArray = new string[0];
14+
public Task<bool> CanWriteAnalyticsCookiesAsync(HttpRequestBase request) => Task.FromResult(false);
1515

16-
// Consent is not necessary and cookies can be written.
16+
public Task<bool> CanWriteSocialMediaCookiesAsync(HttpRequestBase request) => Task.FromResult(false);
1717

18-
public override bool NeedsConsentForNonEssentialCookies(HttpRequestBase request) => false;
19-
20-
public override bool CanWriteNonEssentialCookies(HttpRequestBase request) => true;
21-
22-
// No markdown or scripts will be included.
23-
24-
public override CookieConsentMessage GetConsentMessage(HttpRequestBase request, string locale = null) => null;
25-
26-
public override string GetConsentMarkup(HttpRequestBase request, string locale = null) => string.Empty;
27-
28-
public override IEnumerable<string> GetConsentScripts(HttpRequestBase request, string locale = null) => EmptyStringArray;
29-
30-
public override IEnumerable<string> GetConsentStylesheets(HttpRequestBase request, string locale = null) => EmptyStringArray;
18+
public Task<bool> CanWriteAdvertisingCookiesAsync(HttpRequestBase request) => Task.FromResult(false);
3119
}
32-
}
20+
}

src/NuGetGallery.Core/NuGetGallery.Core.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@
107107
<Compile Include="Authentication\CredentialTypeInfo.cs" />
108108
<Compile Include="Authentication\MicrosoftClaims.cs" />
109109
<Compile Include="Certificates\CertificateFile.cs" />
110-
<Compile Include="Cookies\CookieComplianceServiceBase.cs" />
111-
<Compile Include="Cookies\CookieConsentMessage.cs" />
110+
<Compile Include="Cookies\CookieComplianceService.cs" />
111+
<Compile Include="Cookies\CookieExpirationService.cs" />
112112
<Compile Include="Cookies\ICookieComplianceService.cs" />
113+
<Compile Include="Cookies\ICookieExpirationService.cs" />
113114
<Compile Include="Cookies\NullCookieComplianceService.cs" />
114115
<Compile Include="CoreConstants.cs" />
115116
<Compile Include="CredentialTypes.cs" />

src/NuGetGallery.Core/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@
3131
// The build will automatically inject the following attributes:
3232
// AssemblyVersion, AssemblyFileVersion, AssemblyInformationalVersion, AssemblyMetadata (for Branch, CommitId, and BuildDateUtc)
3333

34-
[assembly: AssemblyMetadata("RepositoryUrl", "https://www.github.com/NuGet/NuGetGallery")]
34+
[assembly: AssemblyMetadata("RepositoryUrl", "https://www.github.com/NuGet/NuGetGallery")]

src/NuGetGallery.Services/ServicesConstants.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public static class ServicesConstants
4646

4747
public const string ApiKeyHeaderName = "X-NuGet-ApiKey";
4848

49+
/// <summary>
50+
/// Parameter for passing the cookie compliance permission.
51+
/// </summary>
52+
public const string CookieComplianceCanWriteAnalyticsCookies = "CanWriteAnalyticsCookies";
53+
4954
public static class ContentNames
5055
{
5156
public static readonly string LoginDiscontinuationConfiguration = "Login-Discontinuation-Configuration";

0 commit comments

Comments
 (0)