Sunday, July 08, 2012

ASP.NET Impersonation and Parallel.ForEach Issue

This week I ran into a very strange issue that has some pretty big implications.

The problem is this: if you use ASP.NET with impersonation, and you also use Parallel.ForEach, threads that run on other cores, lose the execution context.

Put another way, threads that run on other cores don’t respect your impersonation settings, and default to the unimpersonated calling context.

How to reproduce:
First, add impersonation to the web.config (within <system.web>) – for example:

    <identity impersonate=“true“
        userName=“mydomain\MyServiceAccount“ 
        password=“GoodPassword“/>
Now, in code, I run this code synchronously:

    protected void Page_Load(object sender, EventArgs e)
    {
        Debug.WriteLine(“Process starting as “ + WindowsIdentity.GetCurrent().Name);
        List<String> items = new List<string>();
        items.Add(“Item 1″);
        items.Add(“Item 2″);
        items.Add(“Item 3″);
        items.Add(“Item 4″);
        items.Add(“Item 5″);
        items.Add(“Item 6″);
        items.Add(“Item 7″);
 
        foreach (String item in items)
        {
            DoWork(item);
        }
    }
 
    private void DoWork(String itemName)
    {
        DebuWriteLine(“Executing “ + itemName + ” as “ + WindowsIdentity.GetCurrent().Name);
    }
That results in output, like you might think:

    Process starting as myDomain\MyServiceAccount
    Executing Item 1 as myDomain\MyServiceAccount
    Executing Item 2 as myDomain\MyServiceAccount
    Executing Item 3 as myDomain\MyServiceAccount
    Executing Item 4 as myDomain\MyServiceAccount
    Executing Item 5 as myDomain\MyServiceAccount
    Executing Item 6 as myDomain\MyServiceAccount
    Executing Item 7 as myDomain\MyServiceAccount

Now, if you instead run that code as a Parallel.ForEach:

    Parallel.ForEach(items, (item) =>
    {
        DoWork(item);
    });

You will then see very strange results:

    Process starting as myDomain\MyServiceAccount
    Executing Item 2 as myDomain\rseder
    Executing Item 1 as myDomain\MyServiceAccount
    Executing Item 3 as myDomain\rseder
    Executing Item 4 as myDomain\MyServiceAccount
    Executing Item 6 as myDomain\MyServiceAccount
    Executing Item 5 as myDomain\rseder
    Executing Item 7 as myDomain\MyServiceAccount

What is happening? I’m not exactly sure. I read quite a few message board comments of people guessing. I spent a whole afternoon going to a zillion different pages, sorry I don’t have anything specific to reference here.

Anyhow, this code ran on a single processor, quad-core computer. It seems as though when code runs on the other cores, it loses execution context.

A Solution, not a great one though:
One solution I found (again, sorry, I couldn’t find link to credit the original idea) was to RE-impersonate the impersonated user, within our Parallel.ForEach. That looks something like this:

    // Get a handle to the current, impersonated identity
    WindowsIdentity identity = WindowsIdentity.GetCurrent();
 
    Parallel.ForEach(items, (item) =>
    {
        // RE-impersonate the ASP.NET identity, within this separate task
        using (WindowsImpersonationContext impersonationContext =
            identity.Impersonate())

Read more: Rob Seder's Blog

Posted via email from Jasper-Net