Our client is legally required to verify the age of individuals accessing their site. I recently came across a post by Marek about using a rules engine to handle this on a page-by-page basis, rather than through the HttpRequest pipeline. The solution provided by Marek's Post seems ideal for a multi-site setup.
The access verification process is straightforward: a verified cookie is checked, and if it exists, the user is granted access to the site. If the cookie does not exist, the user is redirected to the Age Gate, where their age is verified based on their date of birth input.
With Sitecore Page Rules, you can easily bypass the Age Gate for pages such as the privacy policy, cookie policy, or terms and conditions. Implementing a rules engine also allows you to tailor the access verification or other processes to fit your specific needs.
Source code is very similar to Marek's code with few differences for MVC approach, which are detailed below.
Pipeline patched after "...RequestBegin.StartTracking" in "zzz.Foundation.PageRules.config":
<pipelines>
<mvc.requestBegin>
<processor
type="Foundation.PageRules.Pipelines.RenderLayout.RunPageRules, Foundation.PageRules"
patch:after="processor[@type='Sitecore.Mvc.Analytics.Pipelines.MvcEvents.RequestBegin.StartTracking, Sitecore.Mvc.Analytics']" />
</mvc.requestBegin>
</pipelines>
RunPageRules extends RequestBeginProcessor:
using System;
using Foundation.PageRules.Rules.RuleContext;
using Sitecore;
using Sitecore.Diagnostics;
using Sitecore.Rules;
using Sitecore.Mvc.Pipelines.Request.RequestBegin;
namespace Foundation.PageRules.Pipelines.RenderLayout
{
public class RunPageRules : RequestBeginProcessor
{
private const string PageRulesFieldName = "Page Rules";
public override void Process(RequestBeginArgs args)
{
try
{
var field = Context.Item?.Fields[PageRulesFieldName];
if (field == null || string.IsNullOrWhiteSpace(field.Value))
return;
RuleList<PageRulesRuleContext> rules = RuleFactory.GetRules<PageRulesRuleContext>(field);
if (rules == null || rules.Count == 0)
return;
PageRulesRuleContext ruleContext = new PageRulesRuleContext();
rules.Run(ruleContext);
}
catch (Exception ex)
{
Log.Error("Exception while running page rules", ex, (object)this);
}
}
}
}
Content editor has checkbox for age gate (easier for editors to disable age gate), as well as rules field. Below is an example rule for Age Gate rule. Worth to note Javascript handles age check, cookie assignment and redirect to requested page.
"Where in Live Mode" Condition, ensures we are not in experience editor/preview etc:
using Sitecore.Rules.Conditions;
using Foundation.PageRules.Rules.RuleContext;
namespace Foundation.PageRules.Rules.Conditions
{
public class LiveModeCondition<T> : StringOperatorCondition<T> where T : PageRulesRuleContext
{
protected override bool Execute(T ruleContext)
{
return Sitecore.Context.PageMode.IsNormal
&& !Sitecore.Context.PageMode.IsDebugging
&& !Sitecore.Context.PageMode.IsExploring
&& !Sitecore.Context.PageMode.IsExperienceEditor;
}
}
}
"and except where the device is bot" is Sitecore built in rule under "Device", so device detection should be enabled for this rule to work.
"and where Age Gate is enabled", simply checks value of checkbox on item
using Foundation.PageRules.Rules.RuleContext;
using Sitecore;
using Sitecore.Rules.Conditions;
namespace Foundation.PageRules.Rules.Conditions
{
public class IsAgeGateEnabled<T> : WhenCondition<T> where T : PageRulesRuleContext
{
protected override bool Execute(T ruleContext)
{
return MainUtil.GetBool(Context.Item?.Fields["AgeGateEnabled"]?.Value, false);
}
}
}
"and except where the agegate_cookie cookie exists" checks if cookie with given name already exists. Ideally there should be also added an expiration check, if no session cookie used:
using Foundation.PageRules.Rules.RuleContext;
using Sitecore.Rules.Conditions;
using System.Web;
namespace Foundation.PageRules.Rules.Conditions
{
public class CookieExistsCondition<T> : WhenCondition<T> where T : PageRulesRuleContext
{
public string CookieName { get; set; }
protected override bool Execute(T ruleContext)
{
if (string.IsNullOrEmpty(CookieName))
return false;
return HttpContext.Current.Request.Cookies[CookieName] != null;
}
}
}
"redirect user to age gate Age Gate" action let's us pick Sitecore item where we should redirect to. "dest" query string is used to redirect to requested page after verification:
using System.Linq;
using System.Web;
using Foundation.PageRules.Rules.RuleContext;
using Sitecore;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Links;
using Sitecore.Rules.Actions;
namespace Foundation.PageRules.Rules.Actions
{
public class AgeGateRedirectAction<T> : RuleAction<T> where T : PageRulesRuleContext
{
public ID TargetId { get; set; }
public override void Apply(T ruleContext)
{
if (ID.IsNullOrEmpty(this.TargetId))
{
Log.Warn(string.Format("AgeGateRedirectAction for page '{0}' - no item selected", (object)Context.Item.Paths.FullPath), (object)this);
}
else
{
var obj = Context.Database.GetItem(this.TargetId);
if (obj != null)
{
var urlWithoutParameters = HttpContext.Current.Request.RawUrl.Split('?').First();
var redirectUrl = LinkManager.GetItemUrl(obj);
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["dest"] = System.Net.WebUtility.UrlEncode(urlWithoutParameters);
queryString.Add(HttpContext.Current.Request.QueryString);
HttpContext.Current.Response.Redirect($"{redirectUrl}?{queryString}", true);
}
else
Log.Warn(string.Format("AgeGateRedirectAction for page '{0}' executed without target item", (object)Context.Item.Paths.FullPath), (object)this);
}
}
}
}
- Marek's Post
- Code Companion on Github
- Sitecore Rule Set Editor