RunAs method for running code in specific user SPServiceContext
You may have the need to run some code in a SPServiceContext of a specific user. e.g. when dealing with user profiles or activities.
This can be achieved by using a combination of the SPUser, WindowsIdentity, GenericPrincipal and the GetContext method of SPServiceContext.
From these we can set the HttpContext.Current.User property to the specified user.
Don't forget to set the context back after the code has executed!
Below is a helper method that makes this very easy.
I haven't done any solid testing on performance etc. so the usual caveats apply.
[sourcecode language="csharp"]
// Example calling code
RunAs("http://asharepointsite", "THEDOMAIN\THEUSER", c =>
{
// The code to execute. e.g. get a the UserProfileManager as the passed in user
var upm = new UserProfileManager(c, true);
// Do some stuff to the UPM as the passed in user
});
// Helper method
private static void RunAs(string siteUrl, string userName, Action<SPServiceContext> contextToUse)
{
try
{
var currentSetting = false;
var currentHttpContext = HttpContext.Current;
SPUser currentUser = null;
if (SPContext.Current != null)
{
if (SPContext.Current.Web != null)
{
currentUser = SPContext.Current.Web.CurrentUser;
}
}
SPSecurity.RunWithElevatedPrivileges(delegate
{
using (var site = new SPSite(siteUrl))
{
using (var web = site.OpenWeb())
{
try
{
var user = web.EnsureUser(userName);
currentSetting = web.AllowUnsafeUpdates;
web.AllowUnsafeUpdates = true;
var request = new HttpRequest("", web.Url, "");
HttpContext.Current = new HttpContext(request,
new HttpResponse(
new StringWriter(CultureInfo.CurrentCulture)));
HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
var wi = WindowsIdentity.GetCurrent();
var newfield = typeof (WindowsIdentity).GetField("m_name",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (newfield != null) newfield.SetValue(wi, user.LoginName);
if (wi != null) HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
var elevatedContext = SPServiceContext.GetContext(HttpContext.Current);
contextToUse(elevatedContext);
//Set the HTTPContext back to "normal"
HttpContext.Current = currentHttpContext;
if (currentUser != null)
{
var winId = WindowsIdentity.GetCurrent();
var oldField = typeof (WindowsIdentity).GetField("m_name",
BindingFlags.NonPublic |
BindingFlags.Instance);
if (oldField != null) oldField.SetValue(winId, currentUser.LoginName);
if (winId != null)
HttpContext.Current.User = new GenericPrincipal(winId, new string[0]);
}
}
catch (Exception ex)
{
// Log or whatever
}
finally
{
web.AllowUnsafeUpdates = currentSetting;
}
}
}
});
}
catch (Exception exO)
{
// Log or whatever
}
}
[/sourcecode]