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=mydomainMyServiceAccount
        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 myDomainMyServiceAccount
    Executing Item 1 as myDomainMyServiceAccount
    Executing Item 2 as myDomainMyServiceAccount
    Executing Item 3 as myDomainMyServiceAccount
    Executing Item 4 as myDomainMyServiceAccount
    Executing Item 5 as myDomainMyServiceAccount
    Executing Item 6 as myDomainMyServiceAccount
    Executing Item 7 as myDomainMyServiceAccount

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 myDomainMyServiceAccount
    Executing Item 2 as myDomainrseder
    Executing Item 1 as myDomainMyServiceAccount
    Executing Item 3 as myDomainrseder
    Executing Item 4 as myDomainMyServiceAccount
    Executing Item 6 as myDomainMyServiceAccount
    Executing Item 5 as myDomainrseder
    Executing Item 7 as myDomainMyServiceAccount

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())

        {

            DoWork(item);

        }

    });

This results in the output we expect:

    Process starting as myDomainMyServiceAccount
    Executing Item 1 as myDomainMyServiceAccount
    Executing Item 2 as myDomainMyServiceAccount
    Executing Item 3 as myDomainMyServiceAccount
    Executing Item 4 as myDomainMyServiceAccount
    Executing Item 5 as myDomainMyServiceAccount
    Executing Item 6 as myDomainMyServiceAccount
    Executing Item 7 as myDomainMyServiceAccount

Why is this so bad? Well, imagine I write a shared component that happens to use Parallel.ForEach. Because my methods may be used from an impersonated ASP.NET context, I would need to use the technique above. This seems kind of messy.

You would think the TPL would already account for this. In several posts on various message boards and blogs, people say it “should”, but it doesn’t!

So, unless/until there is a better way to do this, I guess this is a valid workaround?

Posted in .NET 4.0, ASP.NET, ASP.NET MVC, Best-practices, Parallelism, Uncategorized
2 comments on “ASP.NET Impersonation and Parallel.ForEach Issue
  1. Andy Cohen says:

    I just ran into the same problem today and solved it the same way as you. Unfortuately, it appears that ASP.Net behaves differently than a standard desktop app.

    Do you have any updates? I was considering writing a wrapper function/class to do this, but if the work has already been done, I’m all ears.

    Like

  2. Rob Seder says:

    Andy – sorry no, I haven’t run across any better solution – and we went to production with this. Likewise, if you ever come up with anything better, please let me know!! -Rob

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 2 other followers

%d bloggers like this: