I have a WCF service method that calls a SQL stored proc. I'm developing using IIS 5 (can't do much about that, II6/7 not available)
To get some gains, I'm doing a number of async calls to this stored proc by putting the call into a c# TPL Task.
When run as a Task, I'm getting an SQL Exception...
"Login failed. The login is from an untrusted domain and cannot be used with Windows authentication"
However, If I run the exact same process without using a Task, I have no problems with SQL connection
It would appear to me that the credentials for the IIS Virtual folder (WCF) are not being delegated to the Task? Any ideas how I can specificy credentials for the TPL Task thread, ie to use the same as the parent etc ?
I am using Windows Authentication (sspi), and impersonation to be able to connect to the seperate SQL box.
Your help appreciated.
You have two choices.
1) Opt your entire application into always flowing the identity using:
<runtime>
<alwaysFlowImpersonationPolicy enabled="true"/>
</runtime>
This has a side effect of overhead and the danger of accidentally executing some unintended code with the priviledges of the currently calling user rather than the application identity. I would personally avoid this and go with #2 where you explicitly opt-in.
2) Capture the WindowsIdentity
before setting up your TPL tasks and explicitly impersonate where you need to make the calls using Impersonate
+ WindowsImpersonationContext
:
public void SomeWCFOperation()
{
WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
Task.Factory.StartNew(() =>
{
// some unpriviledged code here
using(WindowsImpersonationContext impersonationContext = currentIdentity.Impersonate())
{
// this code will execute with the priviledges of the caller
}
// some more unpriviledged code here
});
}
As another workaround, you can create extensions to the TPL as follows:
public static class TaskFactoryExtensions
{
public static Task StartNewImpersonated(this TaskFactory taskFactory, Action action)
{
var identity = WindowsIdentity.GetCurrent();
return taskFactory.StartNew(() =>
{
using (identity.Impersonate())
{
action();
}
});
}
public static Task<TResult> StartNewImpersonated<TResult>(this TaskFactory taskFactory, Func<TResult> function)
{
var identity = WindowsIdentity.GetCurrent();
return taskFactory.StartNew<TResult>(() =>
{
using (identity.Impersonate())
{
return function();
}
});
}
}
You would then call these new methods in place of the standard StartNew methods.
The downside to this is that there are a lot of methods to override.