June 15, 2017

Suppress Cookies for EU Cookie Compliance

June 15, 2017


Suppress Cookies for EU Cookie Compliance


You've probably seen this already on a number of sites, but increasingly, sites are being required to inform their visitors of the site's cookie policies.  Many implementations that I've seen provide the disclaimer that cookies are being used and that by interacting with the site, the user is providing consent.  My client is going a step further and requires that *no* cookies (except the ASP.NET session cookie) be written to the browser before the user provides consent.

In the case of this client's site, this includes Sitecore's analytics tracking cookie, Sitecore's language cookie (although technically okay to emit because it's only a session cookie) and a host of other tracking cookies emitted from different renderings.

In order to approach this in a way that prevents the need to edit logic across the solution, we came up with a solution that has two primary components:

  1. A Cookie Consent Provider - This provider allows us to interface with a third party system for determining if consent is required, not required or has already been provided.  Additionally, by implementing a provider, we can easily switch out to another provider at some point in the future and not have to re-write logic.  
  2. An MVC filter - By creating an MVC filter that checks for consent, we can simply decorate methods in our controllers to control the emitting of code/cookies without having to affect any logic already contained in those methods.
I'll talk a bit about the primary components and then describe how they all work together to actually suppress the cookies.

Cookie Consent Provider

The provider is based on the standard Microsoft provider model.  I create a base class that all providers will inherit from.  In the base class, I have methods to get and set the user's consent to use cookies.

I won't review the details of my provider implementation but know that it's pretty vanilla with a provider that implements the base class along with the necessary configuration elements in code and the web.config.

MVC Filter

I chose to use an "AuthorizeAttribute" filter to allow me to suppress the execution of controller methods.  The authorize attribute returns a simple "true" or "false" value indicating if the calling method has the required permissions to execute.

The two primary areas to concern ourselves with in the filter are the "AuthorizeCore" and "HandleUnauthorizedRequest" methods.

In "AuthorizeCore", we are checking the need for consent.  It returns a bool value indicating if the current request can be executed.  Also note a bit of tricky logic there regarding the "InvertLogic" parameter.  Basically, I have times when I need to execute the calling method when the result of the "AuthorizeCore" check is false.  By passing in this parameter, I can invert the logic to return the desired results.  In my case, I want to execute a controller rendering that outputs my cookie consent banner when consent is required.  I can just decorate my method with the additional parameter:


In "HandleUnauthorizedRequest" (the code that is executed when "AuthorizeCore" returns "false") we are setting an AuthorizationContext Result.  In my case, we wanted to output an HTML comment that indicates the calling method along with the status so that we could easily confirm that the system is functioning normally when we view the source of the page.

Putting it Together 

As mentioned, the goal here is to suppress cookies from being emitted before the user has given consent.  The two Sitecore-specific cookies that I am blocking are the 'analytics' and 'lang' cookies.  I block these by inserting pipeline processors that check my consent provider.

Note: Always use patch include files and refrain from editing the Sitecore.config file directly.  

It's also worth noting that I have a configurable 'AnalyticsCookiePrefix' parameter that I pass into the "SuppressAnalyticsCookie" processor.  If the cookie name ever changes in the future, we can edit the value in config and not have to touch the code.

In those processors, I make a call to the consent provider to see if I need to suppress the cookies or not.  If I do, I search the current Response and remove the cookies.  If consent has already been provided on a previous request, the code is skipped.

Note: Via Sitecore.Stackexchange, it was recommended that I not disable the Tracker.  I disabled it anyways, because even though it might be legit to have the cookie written on the first load per EU specifications, my client's requirement was that it was not to be written at all until consent is provided.  Additionally, I wanted to avoid editing the web.config file so instead of utilizing the HTTP module method, I wrote a pipeline processor.  (Yeah, I know I edited the web.config with the consent provider . . . )

So the processors take care of the Sitecore-specific cookies that are written.  For any controller methods that end up writing scripts that emit cookies, I simply decorate the method with my Authorize Attribute override and no further code changes are required!

Additional Resources


Ways to block SC_ANALYTICS_GLOBAL_COOKIE from being deployed - Sitecore.stackexchange.com

How to Write a Provider Model

MVC Filters

May 4, 2017

Help! No one can log in!

May 4, 2017

No one can log into Sitecore; Sitecore Domain deleted.

Oh snap.  It's Friday afternoon at the end of a long week and you get a desperate email from the client indicating that none of the logins for the Sitecore CM server work any longer.  Guess I'll put down the beer from the office kegerator and go look at things. :/

First up: Confirm the CD servers are still running/displaying the site.  Check.

Next: Is the CM server up?  Check; I can see the Sitecore login screen.

Next: Attempt to log into the CM with my admin account: Hmm, not able to log in.

Next: RDP into the CM server to see what's going on.  Able to RDP fine.

Next: Look at the logs.  Come across something interesting/scary:


Err, that doesn't look good.  Someone logged into Sitecore using an admin account and managed to delete the Sitecore domain.  You know, the one that *all* the logins are associated with. . .   So now, no one can log in.

Fortunately, the users themselves are not deleted when a domain is deleted. (https://doc.sitecore.net/sitecore_experience_platform/setting_up_and_maintaining/security_and_administration/users_roles_and_domains/create_and_edit_a_security_domain#_Delete_a_domain)

Before we get into how to solve this, let's talk about a couple of security issues here.

One: It looks like users that clearly don't know what they are doing have access to an admin account where they can do very bad things.  I think it should be a extremely rare exception where a content author needs this level of access.  It takes longer to properly configure security so that content authors can do everything they need to do but don't have access to portions of Sitecore that they don't need, but that's how it should be done.  Sometimes you can't avoid this and have to create some admin accounts for users, but as the logs show us, we don't even know where to point the finger since a 'generic' admin account was used.

Two: DO NOT GIVE CREDENTIALS TO GENERIC ADMIN ACCOUNTS!  If a user 'needs' admin access, make the account tied to that user an admin.  This would at least let us see who made the boo-boo.

Okay, let's solve this.

Restoring last night's database backup could be an option.  (Checks with Devops)  "Oh, the database backup process isn't running properly and we have no backups?  Super."  Side note: Well, at least we found that out now and can fix that.

Somehow hacking in to the database to restore whatever was deleted could be an option.  Hmm, I'm not sure I even want to go there.

Recall that there is a "Domains.config" file in the "App_Config\Security" folder and that the error specifically mentioned modifying it, there might be something there. . . .

After comparing the modified file to a stock Sitecore Domains.config file, it was easy to see that the only thing missing was the line that declared the "sitecore" domain:

I added that line back in and poof!: Everyone can log in again.  A much easier solve than I would have thought for something that appeared to be very scary.

Next step: IMMEDIATELY change the generic admin account credentials and let the users know that they can log in again.

Now, back to that beer. . .

TL;DR: Put the "sitecore" domain declaration back into the "Domains.config" file, drink beer.

February 28, 2017

Sitecore Server Switch

February 28, 2017

Sitecore Server Switch

One of the most time-consuming aspects of installing and configuring Sitecore is configuring the servers for their specific roles.  Per Sitecore documentation, specific configuration files should be enabled or disabled for a Content Management (CM) server and likewise, specific files should be enabled or disabled for Content Delivery (CD) server.  Failing to configure the servers for their roles can cause Sitecore to not perform as expected.

Note: There are additional roles for server configurations such as Processing, Reporting and Publishing.

Sitecore's ARM templates look to solve this problem in that they can quickly spin up and configure a Sitecore server, but this only works when using Azure PAAS.  For those not using Azure PAAS, manually adjusting the configuration files can be time consuming.

Sitecore Server Switch looks to solve this issue by using PowerShell.  After installing Sitecore on a server, you can run the "Sitecore_Switch_CM_CD_Lucene_Solr.ps1" script to configure a server as a CM server, as a CD server and additionally configure it to utilize either Lucene or Solr indexing.

Sitecore Server Switch currently supports Sitecore 8.0 (all versions) and Sitecore 8.2 Update 2.  More versions are coming soon.

Sources

Sitecore Server Switch GitHub Source
Sitecore Server Switch on the Sitecore Marketplace (coming soon!)

Acknowledgments

Big shout-out to Sarkis Einarsson for his PowerShell scripts that were the base of this project. (http://sitecoreunleashed.blogspot.com/2015/09/sitecore-8-toggle-cd-configuration.html)

Additional Reading

You can also take this to the next level and automate everything.  Check out Patrick Perrone's work into this space with his automation scripts.

Update (03/01/2017)

And of course, how it usually happens, after writing this module and post, I came across this site that has some automation scripts for 8.2 and even includes the ability to configure additional roles: https://bitbucket.org/sitecoreautomationteam/sitecore-automation/wiki/sitecore-environment
Note: I have not tested this.

And I've also been made aware of Michael West's PowerShell scripts as well: https://gist.github.com/michaellwest/d1124a459cb1fb47486f87d488ecfab8

February 17, 2017

Implement TDS Code Generation for a Helix Generic Repository Foundation Layer

February 17, 2017

Implement TDS Code Generation for a Helix Generic Repository Foundation Layer
I recently sat down to implement some of the suggestions from Phil and Jason's amazing book on Sitecore development, Professional Sitecore 8 Development.  I already had a few Helix architecture implementations under my belt, but felt like I could improve on what I had done.  I think it's probably the case after each implementation that you always find things to improve on for the next implementation. In particular, I was trying to get my Helix architecture to be more consistent, efficient and repeatable.

In chapter 5 of the Professional Sitecore 8 Development book, Phil and Jason speak of improving the architectural design by using design patterns.  Specifically, they recommend creating a generic repository foundation layer.  One of the tools that can help accomplish this goal is Glass Mapper.  Using Glass, we are able to let the repository remain generic and we can tell it the type of object to return at runtime.  In order to constrain the generic repository, their examples set the interface of the generic repository to require that the type being passed in implements "ICmsEntity" (a separate foundation layer that is used to represent a Sitecore item)

So, the "ICmsEntity" interface (code lifted directly from Professional Sitecore 8 Development) looks like this:
And the generic repository (code lifted directly from Professional Sitecore 8 Development)  looks like this:
As you can see, the ICmsEntity interface is empty.  It's really just a way to differentiate Sitecore items from other classes/items.  What we want to do is make sure that any interfaces that we create via Glass that represent Sitecore items implement this ICmsEntity interface.

The example in the book goes on to explain how you can utilize an ORM foundation layer to map to all your Sitecore objects.  In my case, I'm using Team Development For Sitecore (TDS) and have have TDS projects generating my code within my feature layers.

To utilize this generic repository and to have it incorporate the ICmsEntity interface constraint, we need to modify the code generation templates that TDS uses.  This is where it gets super-easy to implement. (wait, it's supposed to be difficult, right?)

First, edit the "glassv3header.tt" T4 template to import the namespace of the foundation layer that contains the ICmsEntity interface.  I'll continue to use the namespaces specified in the example from the book for consistency.  See line #11 below:

Now, edit the "glassv3item.tt" T4 template to have the interfaces that are created implement the ICmsEntity interface.  See line #8 below:

Re-generate your code and you're done!  You now automatically have all your Sitecore objects implementing the ICmsEntity interface and your generic repository will be able to retrieve the proper objects.

Additional Reading/Resources

Professional Sitecore 8 Development - You *do* have the book already, right??
Helix - Helix documentation


January 23, 2017

Setting Sitecore SQL Server Database Permissions

January 23, 2017

Setting Sitecore SQL Server Database Permissions

I recently started on a Sitecore 8.2 project and was setting the SQL Server permissions for the SQL user that I had created for Sitecore to use.  The idea here is to provide least-permissions to the user so that it has enough access to work, but not so much access that it is able to do things that it should not be able to. I feel like I spent way too much time looking for the permissions that should be set.  I eventually found what I was looking for in the installation guide for Sitecore! (#rtfm)

In an effort to make sure this information is accessible (read: so that I find it when I forget next time), I've duplicated the content here.

For the "Master", "Web", "Sessions" and "Analytics" databases, select the following permissions:

  • db_datareader
  • db_datawriter
  • public 

For the Core database, select the following permissions:

  • db_datareader
  • db_datawriter
  • public
  • aspnet_Membership_BasicAccess
  • aspnet_Membership_FullAccess
  • aspnet_Membership_ReportingAccess
  • aspnet_Profile_BasicAccess
  • aspnet_Profile_FullAccess
  • aspnet_Profile_ReportingAccess
  • aspnet_Roles_BasicAccess
  • aspnet_Roles_FullAccess
  • aspnet_Roles_ReportingAccess

For all databases:

In addition to assigning the proper roles to your user for the Sitecore databases, you also want to allow the user to execute stored procedures.  For each database, open the "Properties" window, select "Permissions", select your user and select the "Grant" checkbox for the "Execute" permission.

This is all from the 8.2 Update 2 installation guide, which I can't seem to link to due to authentication requirements on http://dev.sitecore.net, but I think the permissions are the same for previous versions of Sitecore as well.