Sunday, April 17, 2011

Access Denied

SPMetal (Linq to SharePoint) is kind of a mixed bag. On one had it’s great addition to what used to be a very anemic toolset for SharePoint. On the other hand it has some pretty remarkable shortcomings that aren’t really advertised all that well in the documentation.
Two of the more notable ones:

No support for anonymous users (at least at the time of writing)

No support for RunWithElevatedPrivileges

These two shortcomings are actually very intimately related. If you’re using SPMetal (Linq to SharePoint) and you find that code in RunWithElevatedPrivileges still yields Access Denied errors (or doesn’t seem to run in any kind of elevated context) you’ve potentially encountered a very awkward shortcoming of SPMetal.
The good news is that there are some very functional workarounds to these issues but for you to troubleshoot these (and discern where they’re appropriate) it helps to know why these errors happen.

What’s Going On?

For those who have worked with RunWithElevatedPrivileges before you know that certain “shapes” of code don’t work. An example of this would be running some code with RunWithElevatedPrivileges context but then using an object you’d previously created outside the elevated context. Here are two examples of RunRunWithElevatedPrivileges, one which still throws an Access Denied and another that works.
The astute observer will recognize that the below will throw an Access Denied error

The reason the above fails is that the SPContext.Current.Site gets created relatively early in the request lifecycle. By the time you call it from some page/webpart/etc… the SPSite object has long since been constructed, and at the time the SPSite WAS constructed, the application was NOT running in a RunWithElevatedPrivileges security context.

The below DOES work since a new SPSite (and all other objects) are created in the RunWithElevatedPrivileges security context.

The moral of the story is that objects you’re working with need to be recreated within your RunWithElevatedPrivileges code block.

The Problem with SPMetal

The two problems we kicked off this post with:

No support for anonymous users (at least at the time of writing)

No support for RunWithElevatedPrivileges

Actually stem from the same class of problem as the incorrect “shape” of code shown in the RunWithElevatedPrivileges example above. When the Linq to SharePoint Provider goes to create a connection to the given SPSite/SPWeb, it’ll take the current SPSite object out of the SPContext.Current context. The problem with this is that there’s no (easy) way to have that SPSite object constructed with an elevated security context.

The trick is to set the HttpContext to null temporarily just prior to creating creating the DataContext in a RunWithElevatedPrivileges block. This bullies the Linq to SharePoint provider into creating a new SPSite, and this one is created with the correct context.

Below is a helper method which follows the RunWithElevatdPrivileges pattern which allows you to create a DataContext that will indeed run with elevated privileges.

/// <summary>/// Runs with elevated priviledges after setting the HttpContext temporarily to null prior to doing so. This allows linq to sql /// to create a DataContextManager based on the RunWithElevatedCredential (farm account) instead of the current user./// Otherwise the linq2sql DataContextManager would take its SPSite off of the SPContext.Current.Site which is already cached/// with the priviledges of the current user./// /// NOTE: This should ONLY BE CALLED by code creating Linq to SharePoint SharePointDataContexts. It's also of note that any/// SharePointDataContext created using this code WILL BE ABLE TO DO ANYTHING the farm account can./// </summary>protectedstaticvoid RunWithElevatedPrivilegesAndContextSwitch(SPSecurity.CodeToRunElevated secureCode)
{
HttpContext backupContext = HttpContext.Current;
HttpContext.Current = null;
SPSecurity.RunWithElevatedPrivileges(secureCode);
HttpContext.Current = backupContext;
}

It’s worth mentioning that if your data access is consolidated in to a formal data access layer you may need to centralize this kind of code so that it doesn’t get placed all over the site, but above is the central idea behind getting Linq to Sharepoint DataContexts to run with Farm Account privileged access.

About Me

Tyler Holmes is a Solutions Architect working in Portland, Oregon. He lives mostly in the MS tech stack and is currently treading the waters of Communication/Collaboration and Business Intelligence with off the shelf/open source technologies.