update the cookie consent control.
This commit is contained in:
@ -31,6 +31,8 @@
|
||||
@inject IUrlMappingRepository UrlMappingRepository
|
||||
@inject IVisitorRepository VisitorRepository
|
||||
@inject IJwtManager JwtManager
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject ISettingService SettingService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -107,6 +109,7 @@
|
||||
private string _styleSheets = "";
|
||||
private string _scripts = "";
|
||||
private string _message = "";
|
||||
private bool _allowCookies;
|
||||
private PageState _pageState;
|
||||
|
||||
// CascadingParameter is required to access HttpContext
|
||||
@ -140,6 +143,9 @@
|
||||
_prerender = site.Prerender;
|
||||
_fingerprint = site.Fingerprint;
|
||||
|
||||
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
|
||||
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
|
||||
|
||||
var modules = new List<Module>();
|
||||
|
||||
Route route = new Route(url, alias.Path);
|
||||
@ -170,7 +176,7 @@
|
||||
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
|
||||
}
|
||||
|
||||
if (site.VisitorTracking)
|
||||
if (site.VisitorTracking && _allowCookies)
|
||||
{
|
||||
TrackVisitor(site.SiteId);
|
||||
}
|
||||
@ -245,7 +251,8 @@
|
||||
ReturnUrl = "",
|
||||
IsInternalNavigation = false,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = true
|
||||
Refresh = true,
|
||||
AllowCookies = _allowCookies
|
||||
};
|
||||
}
|
||||
else
|
||||
|
@ -19,10 +19,22 @@ namespace Oqtane.Controllers
|
||||
_cookieConsentService = cookieConsentService;
|
||||
}
|
||||
|
||||
[HttpGet("CanTrack")]
|
||||
public async Task<bool> CanTrack()
|
||||
[HttpGet("IsActioned")]
|
||||
public async Task<bool> IsActioned()
|
||||
{
|
||||
return await _cookieConsentService.CanTrackAsync();
|
||||
return await _cookieConsentService.IsActionedAsync();
|
||||
}
|
||||
|
||||
[HttpGet("CanTrack")]
|
||||
public async Task<bool> CanTrack(string optout)
|
||||
{
|
||||
return await _cookieConsentService.CanTrackAsync(bool.Parse(optout));
|
||||
}
|
||||
|
||||
[HttpGet("CreateActionedCookie")]
|
||||
public async Task<string> CreateActionedCookie()
|
||||
{
|
||||
return await _cookieConsentService.CreateActionedCookieAsync();
|
||||
}
|
||||
|
||||
[HttpGet("CreateConsentCookie")]
|
||||
@ -30,5 +42,11 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
return await _cookieConsentService.CreateConsentCookieAsync();
|
||||
}
|
||||
|
||||
[HttpGet("WithdrawConsentCookie")]
|
||||
public async Task<string> WithdrawConsentCookie()
|
||||
{
|
||||
return await _cookieConsentService.WithdrawConsentCookieAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,5 @@ namespace Oqtane.Extensions
|
||||
|
||||
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
|
||||
=> builder.UseMiddleware<ExceptionMiddleware>();
|
||||
|
||||
public static IApplicationBuilder UseCookieConsent(this IApplicationBuilder builder)
|
||||
=> builder.UseMiddleware<CookieConsentMiddleware>();
|
||||
}
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
internal class CookieConsentMiddleware
|
||||
{
|
||||
private readonly IList<string> _defaultEssentialCookies = new List<string>
|
||||
{
|
||||
".AspNetCore.Culture",
|
||||
"X-XSRF-TOKEN-COOKIE",
|
||||
".AspNetCore.Identity.Application"
|
||||
};
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public CookieConsentMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
// check if framework is installed
|
||||
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
||||
var settingService = context.RequestServices.GetService(typeof(ISettingService)) as ISettingService;
|
||||
var cookieConsentService = context.RequestServices.GetService(typeof(ICookieConsentService)) as ICookieConsentService;
|
||||
string path = context.Request.Path.ToString();
|
||||
|
||||
if (config.IsInstalled())
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = (Dictionary<string, string>)context.Items[Constants.HttpContextSiteSettingsKey];
|
||||
if (settings != null)
|
||||
{
|
||||
var cookieConsentEnabled = bool.Parse(settingService.GetSetting(settings, "CookieConsent", "False"));
|
||||
if (cookieConsentEnabled && !await cookieConsentService.CanTrackAsync())
|
||||
{
|
||||
//only allow essential cookies when consent is not granted
|
||||
var loginCookieName = settingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
var cookiesSetting = settingService.GetSetting(settings, "EssentialCookies", string.Empty);
|
||||
|
||||
var essentialCookies = !string.IsNullOrEmpty(cookiesSetting) ? cookiesSetting.Split(",").ToList() : _defaultEssentialCookies;
|
||||
|
||||
foreach (var cookie in context.Request.Cookies)
|
||||
{
|
||||
if (cookie.Key != loginCookieName && !essentialCookies.Contains(cookie.Key))
|
||||
{
|
||||
context.Response.Cookies.Delete(cookie.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// continue processing
|
||||
if (_next != null) await _next(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -12,27 +15,106 @@ namespace Oqtane.Services
|
||||
public class ServerCookieConsentService : ICookieConsentService
|
||||
{
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly CookiePolicyOptions _cookiePolicyOptions;
|
||||
|
||||
public ServerCookieConsentService(IHttpContextAccessor accessor)
|
||||
public ServerCookieConsentService(IHttpContextAccessor accessor, IOptions<CookiePolicyOptions> cookiePolicyOptions)
|
||||
{
|
||||
_accessor = accessor;
|
||||
_cookiePolicyOptions = cookiePolicyOptions.Value;
|
||||
}
|
||||
|
||||
public Task<bool> CanTrackAsync()
|
||||
public Task<bool> IsActionedAsync()
|
||||
{
|
||||
var consentFeature = _accessor.HttpContext?.Features.Get<ITrackingConsentFeature>();
|
||||
var canTrack = consentFeature?.CanTrack ?? true;
|
||||
var actioned = false;
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
var cookieValue = GetCookieValue("actioned");
|
||||
actioned = cookieValue == Constants.CookieConsentActionCookieValue;
|
||||
}
|
||||
return Task.FromResult(actioned);
|
||||
}
|
||||
|
||||
public Task<bool> CanTrackAsync(bool optOut)
|
||||
{
|
||||
var canTrack = true;
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
var cookieValue = GetCookieValue("consent");
|
||||
var saved = cookieValue == Constants.CookieConsentCookieValue;
|
||||
if (optOut)
|
||||
{
|
||||
canTrack = string.IsNullOrEmpty(cookieValue) || !saved;
|
||||
}
|
||||
else
|
||||
{
|
||||
canTrack = cookieValue == Constants.CookieConsentCookieValue;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(canTrack);
|
||||
}
|
||||
|
||||
public Task<string> CreateActionedCookieAsync()
|
||||
{
|
||||
var cookieString = CreateCookieString(false, string.Empty);
|
||||
return Task.FromResult(cookieString);
|
||||
}
|
||||
|
||||
public Task<string> CreateConsentCookieAsync()
|
||||
{
|
||||
var consentFeature = _accessor.HttpContext?.Features.Get<ITrackingConsentFeature>();
|
||||
consentFeature?.GrantConsent();
|
||||
var cookie = consentFeature?.CreateConsentCookie() ?? string.Empty;
|
||||
var cookieString = CreateCookieString(true, Constants.CookieConsentCookieValue);
|
||||
return Task.FromResult(cookieString);
|
||||
}
|
||||
|
||||
return Task.FromResult(cookie);
|
||||
public Task<string> WithdrawConsentCookieAsync()
|
||||
{
|
||||
var cookieString = CreateCookieString(true, string.Empty);
|
||||
return Task.FromResult(cookieString);
|
||||
}
|
||||
|
||||
private string GetCookieValue(string type)
|
||||
{
|
||||
var cookieValue = string.Empty;
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
var value = _accessor.HttpContext.Request.Cookies[Constants.CookieConsentCookieName];
|
||||
var index = type == "actioned" ? 1 : 0;
|
||||
cookieValue = !string.IsNullOrEmpty(value) && value.Contains("|") ? value.Split('|')[index] : string.Empty;
|
||||
}
|
||||
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
private string CreateCookieString(bool saved, string savedValue)
|
||||
{
|
||||
var cookieString = string.Empty;
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
var savedCookie = saved ? savedValue : GetCookieValue("consent");
|
||||
var actionedCookie = Constants.CookieConsentActionCookieValue;
|
||||
var cookieValue = $"{savedCookie}|{actionedCookie}";
|
||||
var options = _cookiePolicyOptions.ConsentCookie.Build(_accessor.HttpContext);
|
||||
|
||||
if (!_accessor.HttpContext.Response.HasStarted)
|
||||
{
|
||||
_accessor.HttpContext.Response.Cookies.Append(
|
||||
Constants.CookieConsentCookieName,
|
||||
cookieValue,
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = options.Expires,
|
||||
IsEssential = true,
|
||||
SameSite = options.SameSite,
|
||||
Secure = options.Secure
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//get the cookie string from response header
|
||||
cookieString = options.CreateCookieHeader(Constants.CookieConsentCookieName, Uri.EscapeDataString(cookieValue)).ToString();
|
||||
}
|
||||
|
||||
return cookieString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,13 +169,6 @@ namespace Oqtane
|
||||
options.CustomSchemaIds(type => type.ToString()); // Handle SchemaId already used for different type
|
||||
});
|
||||
services.TryAddSwagger(_useSwagger);
|
||||
|
||||
//add cookie consent policy
|
||||
services.Configure<CookiePolicyOptions>(options =>
|
||||
{
|
||||
options.CheckConsentNeeded = context => true;
|
||||
options.ConsentCookieValue = Constants.CookieConsentCookieValue;
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
@ -232,8 +225,6 @@ namespace Oqtane
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseAntiforgery();
|
||||
app.UseCookiePolicy();
|
||||
app.UseCookieConsent();
|
||||
|
||||
if (_useSwagger)
|
||||
{
|
||||
|
@ -121,6 +121,16 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* cookie consent */
|
||||
.gdpr-consent-bar .btn-show{
|
||||
bottom: 0;
|
||||
right: 5px;
|
||||
}
|
||||
.gdpr-consent-bar .btn-hide{
|
||||
top: 0;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.main .top-row {
|
||||
display: none;
|
||||
|
@ -100,6 +100,16 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* cookie consent */
|
||||
.gdpr-consent-bar .btn-show{
|
||||
bottom: 0;
|
||||
right: 5px;
|
||||
}
|
||||
.gdpr-consent-bar .btn-hide{
|
||||
top: 0;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
||||
.app-menu {
|
||||
|
Reference in New Issue
Block a user