Tuesday, March 13, 2007

Impersonation in Event Handlers

I have been using impersonation in event handlers, and thought I should share with you my prefered method.

I am working with the wonderful example of Victor & Julien Lepine of how to do impersonation.
However, there is one thing to beware of. In event handlers, the best way to get the referance to the list item, the list and the web site the event has happened in is use the "properties" object that we recieve as parameter:

public override void ItemUpdated(SPItemEventProperties properties)
{
    
ImpersonationUtility imp = ImpersonationUtility.ImpersonateAdmin();
    
using (SPWeb web = properties.OpenWeb())
    {
        
//my code here 
        string username = web.CurrentUser.Name;
    }
    imp.Undo();
    
base.ItemUpdated(properties);
}

Looks ok? the answer is no. Even though it seems we are impersonating administrator in the line before we are opening the SPWeb object, it is still opened with the credentials of the user who triggered the event. Why? because the "SPItemEventProperties properties " object was created with those credentials.

What to do?:
I came up with a roundabout way of getting the work done:

ImpersonationUtility imp = null;
try
{
    imp = 
ImpersonationUtility.ImpersonateAdmin();
    
using (SPWeb webOrigUser = properties.OpenWeb())
    {
        
//get the token for the impersonation user (this will get the user that the ImpersonationUtility is using)
        SPUserToken token = webOrigUser.AllUsers[WindowsIdentity.GetCurrent().Name].UserToken;
        
using (SPSite site = new SPSite(properties.SiteId, token))
        {
            
using (SPWeb web = site.OpenWeb(properties.RelativeWebUrl))
            {
                
            }
        }
    }
}
catch{}

What does that code do? We impersonate (as usual) and then get the SPWeb object from the properties. We use it to get an "SPUserToken" object for the impersonation user, and then we are opening an SPSite object with that token. The SPWeb object we open from that SPSite (note how I give it the relative url to open) is opened with the correct credentials of the impersonating user.

This solved all my impersonation problems. How about you?

1 comment:

AndersR said...

I use a similar approach (instantiating new SPSite using properties.SiteId and going from there) but i dont get why you would use the "old" way of impersonating with unmanaged code when you got SPSecurity.RunWithElevatedPrivileges() ?

In WSSv2 it was always a hassle impersonating admin, but in WSSv3 its easy

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(web.Site.ID))
{
// do things assuming the permission of the "system account"
}
});

Oh yeah and you can test as well if you have an impersonated session or if the current users credentials is "owning" the object by checking Impersonating property on SPSite

cheers
Anders Rask