Thursday, May 27, 2010

Using WindowsIdentity.Impersonate in SharePoint applications

Let's say that you have a SharePoint workflow that needs to call a web service and that the web service needs to be called using the identity of the user that started the workflow.

Unfortuately, workflows run don't run as the user who started the workflow instance, so you can't just use the current context's user.

I found the following in a blog posting that has since been deleted. (I rescued it from the Google cache.)

Wanted to record this so I can come back to it later....

0) Get the user that started the workflow from SPWorkflowActivationProperties.

Convert the SPUser to a WindowsIdentity. To accomplish this, create the user principal name (UPN) for the user. Then instantiate a new WindowsIdentity using the UPN.

private static string GetUpn(SPUser spUser)
{
string[] tmp = spUser.LoginName.Split('\\');
System.DirectoryServices.ActiveDirectory.Domain domain =
System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();
return tmp[1] + "@" + domain.Name;
}
.
.
.
SPUser spUser = GetMySPUser();
WindowsIdentity spUserIdentity = new WindowsIdentity(GetUpn(spUser));

Restrictions: This approach will only work when the user is actually a member of the Domain.GetCurrentDomain. Otherwise you need to search for the domain name through the active directory service. Also this approach will only work if the process user has the local security policy “Act As Part of the Operating System” = “true”. By default the NT Authority\Network Service user has this privilege. Additionally the WindowsIdentity instantiation is also restricted to the Server 2003 or Sever 2008 operating systems, but so is SharePoint.

Next perform the impersonation.

WindowsImpersonationContext context = spUserIdentity.Impersonate();
try {
MyLegacyData data = ExecuteLegacyLogic();
using (SPSite site = new SPSite(mySiteId))
{
using (SPWeb web = site.OpenWeb(myWeb)
{
AddLegacyDataResults( web, data );
}
}
}
finally
{
context.Undo();
}

Notes: In the above block the SPSite object access is the same as if were performed using new SPSite(mySiteId, spUser.UserToken).

Restrictions: Never run this code block inside a SPSecurity.RunWithElevatedPrivileges delegate. This appears to be unsupported by SharePoint. The resulting SPSite and SPWeb objects will be unusable. In this condition, a StackOverflowExcetion will be thrown if you attempt to access an SPUser in the SPWeb.Users collection and will bring down the entire process.

Outside of the above restriction, the code appears to work as expected.