Monday, August 24, 2009

Delegates and SPSecurity.RunWithElevatedPrivileges()

ASP.Net provides a powerful way of doing things without actually knowing the meaning of code.
It is quite common to get a code stub from seniors for a particular purpose and pasting it inside own class, changing the logic and pressing F5!!! :) ... Visual Studio always rocks...
When I first started learning SharePoint, the code stub given to me by my seniors, always looks very peculiar to me!!! Specially the following line -

using (SPSite ospSite = new SPSite(ConfigurationManager.AppSettings["SiteURL"].ToString()))
ospWeb = ospSite.OpenWeb();

So lets drill down it. First comes Delegates.

A delegate is a type that references a method. Once a delegate is assigned a method, it behaves exactly like that method. The delegate method can be used like any other method, with parameters and a return value, as in this example:

public delegate int PerformCalculation(int x, int y);

Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.
This ability to refer to a method as a parameter makes delegates ideal for defining callback methods. For example, a sort algorithm could be passed a reference to the method that compares two objects. Separating the comparison code allows the algorithm to be written in a more general way.

Delegates Overview

Delegates have the following properties:
Delegates are similar to C++ function pointers, but are type safe.
Delegates allow methods to be passed as parameters.
Delegates can be used to define callback methods.
Delegates can be chained together; for example, multiple methods can be called on a single event.
Methods don't need to match the delegate signature exactly.
C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.

Now, Here comes an exciting question : When to Use Delegates Instead of Interfaces ?

Both delegates and interfaces allow a class designer to separate type declarations and implementation. A given interface can be inherited and implemented by any class or struct; a delegate
can created for a method on any class, as long as the method fits the method signature for the delegate. An interface reference or a delegate can be used by an object with no knowledge of the class that implements the interface or delegate method. Given these similarities, when should a class designer use a delegate and when should they use an interface?

Use a delegate when:
An eventing design pattern is used.
It is desirable to encapsulate a static method.
The caller has no need access other properties, methods, or interfaces on the object implementing the method.
Easy composition is desired.
A class may need more than one implementation of the method.

Use an interface when:
There are a group of related methods that may be called.
A class only needs one implementation of the method.
The class using the interface will want to cast that interface to other interface or class types.
The method being implemented is linked to the type or identity of the class: for example, comparison methods.

Now, here comes the main subject of today's post: running code in sharepoint with elevated rights, an operation sometimes required, sometimes abused and often misunderstood.
The WSS Object Model provides a huge number of classes, some of which can carry-out potentially dodgy actions, so require elevation to run. Ordinarily you'd just deal with this by logging in as a user with rights to carry out the operation, but occasionally this isn't practical or possible, and that's where today's subject comes in.
Let's say, just as an example, you're creating an anonymously-accessible site. In a Control on on of the pages you want to enumerate subsites of your site, and grab some properties thereof, maybe for display, maybe for some other operation in your code - however, this isn't something an anonymous identity can do.
In steps our hero - RunWithElevatedPrivileges()
Used correctly, this method allows a specified block of code to run in the context of the SharePoint System Account, a powerful method with much potential. Here's the summary from the SDK:

[SharePointPermissionAttribute(SecurityAction.Demand, Impersonate=true)] [SharePointPermissionAttribute(SecurityAction.Demand, ObjectModel=true)] public static void RunWithElevatedPrivileges ( CodeToRunElevated secureCode)

Now the CodeToRunElevated parameter can be a reference to a void, parameterless method or an anonymous method via delegate() - please, follow the SDK link if that's unclear.
Pretty simple, huh? Yep, well as always there's a catch or two.

1. If you're manipulating any Object Model elements within your elevated method, you need to get a fresh SPSite reference inside this call. For example

SPSite mySite = new SPSite(
SPWeb myWeb = SPSite.OpenWeb();
// further implementation omitted

2. You can't just use SPContext.Current.Site to get your SPSite reference - or you'll ber handed the object with the security context of the anonymous (or non-elevated) user and your elevation will not work as expected.

3. If you need to Update() anything inside this block, you'll need to call SPSite.AllowUnsafeUpdates() on your new site reference (or web reference) as per this SDK entry.

So those are the gotchas. Following those we have the obvious security warnings - be careful what you do within this call, as the system identity has full control over SharePoint and could do Very Bad Things if incorrectly used. Sanitise any user input very carefully if you're going to let it anywhere near this method - you certainly don't want a user finding some injectable exploit into this code. Exercise caution over what you do, for this power must be used wisely.

1 comment: