Tasks and Impersonation
-
Wednesday, July 21, 2010 7:49 PM
From the documentation I am not sure I understand how WindowsIdentity is maintained across threads when using Task objects in .NET 4 (TPL).
Do I need to wrap each delegate I want to run with another delegate that Impersonates again? E.g I defined these two helper methods:
// use to wrap impersonation code around a void method
void Impersonate(Action action, WindowsIdentity identity) {
WindowsImpersonationContext impersonation = null;
if (identity != null)
impersonation = identity.Impersonate();
try {
action();
}
finally {
if (impersonation != null)
impersonation.Undo();
}
}
// use to wrap impersonation code around a function
TResult Impersonate<TResult>(Func<TResult> func, WindowsIdentity identity) {
TResult result;
WindowsImpersonationContext impersonation = null;
if (identity != null)
impersonation = identity.Impersonate();
try {
result = func();
}
finally {
if (impersonation != null)
impersonation.Undo();
}
return result;
}
Or can I simply create the Task object in the impersonated context and it will automatically execute under that impersonation even if the scheduler runs it on some ThreadPool thread later. Like this:
WindowsIdentity identity = <some logon user code>;
WindowsImpersonationContext impersonation = identity.Impersonate();
try {
Task.Factory.StartNew(() => {
... code that may use parameters from current context...
});
}
finally {
impersonation.Undo();
}I would be grateful for any pointers,
Karl
All Replies
-
Friday, July 23, 2010 4:50 AMOwner
Hi Karl-
By default, if you create the Task object in the impersonated context, when that Task later runs it will do so under the same impersonated context. This happens for you automatically because of how Task utilizes ExecutionContext, which includes SecurityContext, which includes the WindowsIdentity. The ExecutionContext is captured when the Task is created, and the body of the task runs under that ExecutionContext.
I hope that helps.
- Proposed As Answer by Stephen Toub - MSFTMicrosoft Employee, Owner Friday, July 23, 2010 4:50 AM
- Marked As Answer by kwaclaw Friday, July 23, 2010 5:15 PM
-
Friday, July 23, 2010 5:15 PM
Hi Karl-
By default, if you create the Task object in the impersonated context, when that Task later runs it will do so under the same impersonated context. This happens for you automatically because of how Task utilizes ExecutionContext, which includes SecurityContext, which includes the WindowsIdentity. The ExecutionContext is captured when the Task is created, and the body of the task runs under that ExecutionContext.
I hope that helps.
Yes, that helps. Thank you very much!
-
Tuesday, August 02, 2011 7:17 PM
I can confirm that this is untrue for ASP.NET MVC apps configured with ASP.NET Impersonation. Consider the following code:
(The caller is DOMAIN\myUserAccount)
(The MVC app runs in an app pool as DOMAIN\myServiceAccount)
public class MyController : Controller { private static readonly ILog LOG = //init code public ActionResult DoTasks() { LOG.DebugFormat("I am running as {0}", WindowsIdentity.GetCurrent().Name); Task a = TaskFactory.StartNew(this.CreateTask("A")); Task b = TaskFactory.StartNew(this.CreateTask("B")); Task c = TaskFactory.StartNew(this.CreateTask("C")); Task.WaitAll(new Task[] { a, b, c }); // returns an ActionResult } public Task CreateTask(string taskName) { LOG.DebugFormat("Creating task {0} as {1}", taskName, WindowsIdentity.GetCurrent().Name)); return new Task(() => { LOG.DebugFormat("I am running as {0}", WindowsIdentity.GetCurrent().Name); }); } }
The output of this code is:
I am running as DOMAIN\myUserAccount
Creating task A as DOMAIN\myUserAccount
Creating task B as DOMAIN\myUserAccount
I am running as DOMAIN\myServiceAccount
Creating task C as DOMAIN\myUserAccount
I am running as DOMAIN\myServiceAccount
I am running as DOMAIN\myServiceAccount
-
Tuesday, August 02, 2011 7:39 PMOwnerjar349, in your example, what's the value of ExecutionContext.IsFlowSuppressed()?
-
Tuesday, August 02, 2011 8:52 PM
Stephen,
Where in my code would you like me to read the value? Anywhere? Or somewhere specific?
Let me know!
~ jR
-
Tuesday, August 02, 2011 11:41 PMOwner
Hi jR-
At the beginning of your DoTasks method would be a good place to check.
Also, could you try adding the following to your configuration file:
<configuration>
<runtime>
<alwaysFlowImpersonationPolicy enabled="true"/>
</runtime>
</configuration>Thanks.
-
Wednesday, August 03, 2011 3:39 PM
Stephen,
Thanks for continuing to track this issue - I really appreciate your help. I placed the call in my DoTasks method and added the runtime configuration snippet to my web.config.
On execution, the value is false and the behavior that I originally described still occurs.
Inside my real tasks, I am delegating the user's credentials to SQL Server. I didn't want to tell you this because I feared it might lead you to think that the problem is with my configuration of constrained delegation. When I change my code from using TPL to being synchronous, I get results from the database because everything is correctly configured. But I think I should mention it now in case there's some strange gotcha about SSPI being unable to pick up the impersonation context for some reason.
I think you're having me check IsFlowSuppressed() to ensure that the SecurityContext is correctly flowing to my worker threads. That does seem to be the case even though IsFlowSuppressed() = false. What other environmental factors could inhibit the flow of my main thread's ExecutionContext onto TPL threads?
Thanks!!
~ jR
-
Wednesday, August 03, 2011 4:54 PM
Stephen,
Here's an interesting thing: SecurityContext.IsWindowsIdentityFlowSuppressed() is True!
How can I resume that flow?
Thanks!
~ jR
-
Wednesday, August 03, 2011 7:39 PM
Here is a work-around that will work: inside of the lambda, impersonate the user that ASP.NET has already impersonated for you.
public Action CreateTask(string taskName) { WindowsIdentity impersonatedUser = WindowsIdentity.GetCurrent(); return () => { using (WindowsImpersonationContext ctx = impersonatedUser.Impersonate()) { LOG.DebugFormat("I am running as {0}", WindowsIdentity.GetCurrent().Name); } }; }
~ jR
- Proposed As Answer by jar349 Wednesday, August 03, 2011 7:40 PM
-
Thursday, August 04, 2011 5:27 PMDo you by any chance have the legacyImpersonationPolicy config switch enabled?
-
Thursday, August 04, 2011 5:50 PMI do not. Would you like me to explicitly set it to false?
-
Tuesday, October 25, 2011 5:53 PM
Hello Stephen,
I'm running an impersonation issue in my app.
I was reading this post and I see that you are saying that:
By default, if you create the Task object in the impersonated context, when that Task later runs it will do so under the same impersonated context
Now, this is my scenario. I'm running a web application, I have the web.config:
<identity impersonate="true" userName="myuser" password="mypass" />
In the global.asax in the app_starts method I'm doing sth like this://Watching here the WindowsIdentity.GetCurrent().Name //I get "myuser" Task.Factory.StartNew(new Action(()=>{ //Watching here the WindowsIdentity.GetCurrent().Name //I get the default asp.net user }));
If I modify the values of the runtime element in the Framework .config file like this:<legacyImpersonationPolicy enabled="true"/> <alwaysFlowImpersonationPolicy enabled="false"/>
Then I will get the expected result. The issue is that I cant modify those values since it is a shared web server, and I cant override those values in my web.config since those are not taken into consideraiton (I tested adding the <runctime> element in the web.config and those values are not making any difference in the execution).Then I finally used the approach explained bellow by jar349 by explicitly impersonating inside the delegate.
Now my question is probably more related to what happens with the reference to the WindowsIdentity that the delegate is accessing? Is that creating a string dependency between the main thread and the created task?
Thanks,
Fernando
-
Wednesday, May 30, 2012 2:08 PMStephen - Is it possible to impersonate in a custom scheduler? I created a TaskScheduler with a single thread executing under a different user context. As you stated, the task was executed under the context it was created and not the thread of the scheduler. I understand, just curious if there is a way to impersonate the threaded execution rather than the individual task creations.
- Edited by rcardare Wednesday, May 30, 2012 3:19 PM

