Every now and again, I like to add a post to my blog to remind me of something useful. If anyone else finds it useful, then all the better. I'll come and tidy this one up a bit later.
I write alot of ASP.Net applications at the moment. I'm a big fan of REST (
REpresentational State Transfer) and AJAX too, and have used them alot recently to implement some nice solutions.
When connecting to data sources, the most common model I've encountered is the Trusted User model. In this model, the application runs in the context of a user who is trusted to access network resouces like fileshares and databases. This is not to say that users do not log on to the applications; it's just that any actions they perform that require the application to connect to a resource is usually done in the context of this trusted user. The other common model is Impersonation/Delegation, which tends to involve logging on users then using their credentials to access resources (i.e. the user is impersonated by the application, and then delegation is used to allow the application to present their credentials to
remote resources).
This is all fine and dandy. You can set up either model at the application level by adding the
<identity/> element in web.config, which, amongst other things, allows you to define a specific identity to run the user as. Impersonation/Delegation required a little more work if you want the credentials to survive numerous resource hops; If I remember correctly, your domain has to be native (all Windows 2000 or above with "Kerberos" authentication), and the server the application is hosted on has to have a flag set in Active Directory to allow it to be trusted for delegation.
However, when you start the application up and start a timed event or create a separate process thread (the two are basically the same thing), then things start to break. If you look at the thread identity when the timer elapses and its event fires, or some code executes in the child thread, you find it is null. This is annoying, because it means that your thread/timer event cannot make calls to other resources that require authentication.
What you need to do is grab the identity that the application starts with. You can do this most simply in Global.asax, in the Applcation_OnStart event by storing it in a field, by using
m_ImpersonationIdentity = WindowsIdentity.GetCurrent();
(you'll need to be using System.Security and System.Security.Principal).
You can then pass this identity to your other event/timer when you instantiate or start them and store them in a field there. When you want to make a remote call, you change the impersonation context:
//Create a principal
WindowsPrincipal currentPrincipal = new WindowsPrincipal(m_ImpersonationIdentity);
//Assign the principal
System.Threading.Thread.CurrentPrincipal = currentPrincipal;
//Begin an impersonation context.
WindowsImpersonationContext impersonationContext = m_ImpersonationIdentity.Impersonate();
try{
//Do Stuff
}
finally{
//Undo the impersonation context.
impersonationContext.Undo();
//"unassign" the thread principal - very important - think thread pooling.
System.Threading.Thread.CurrentPrincipal = null;
}
(Again, you'll need to be using System.Security and System.Security.Principal).
Et voila, you can now make calls in the context of whatever identity you want too when launching child threads in an ASP.Net application.