I am still a bit confused with the terminology here. But I think you are in trouble :)
In my opinion/understanding the following two statements already tell you that what you want is impossible:
I've been told that for each page that
connects to our SQL server, I need to
make it so that the user connects
using an Active Directory account.
versus
However, the powers that be here don't
like impersonation;
I would say the first statement implies impersonation. I believe this to be a very common requirement by the way. Mainly for exactly the reason you stated: it lets you see who is doing what on your database. It might even be (or become) requested by law... You usually do not have to give permissions to individuals though, I tend to use groups/roles.
Now in your comment to @Mauro you write
I have a cookie which contains the AD
account I want to execute the SQL
query as. My only question is, how do
I make the asp.net process run the SQL
query as the identity specified by the
cookie?
What do you mean by "contains the AD account"? Just the username or username and password? If you have both you can (must?) follow the MSDN article (WindowsImpersonationContext) mentioned by @Jeff Siver (or is this what you and your management describe as "cluttering the code"?).
If you do not have the password you are out of luck. How would you possibly run something with the privileges of a certain user without providing the system any clue (but a username) that you are allowed to do so? I cannot see at the moment how a cookie could help here. Unless - hmm - maybe you could roll your own Security Principal? That is above me, though.
In case we do not (yet) have the same understanding of some terms: this would be the (or rather my) list of "usual suspects" capable of producing misunderstandings.
- Windows Authentication: the option in IIS vs. Windows Integrated Authentication in SQL Server
- Impersonation: the ASP.NET option (as stated by @JustLoren) vs. impersonation in code ("cluttered code"?)
- Delegation: the passing on of credentials from IIS to SQL Server (for example) vs. triple-hop (passing of credentials from IE (or browser) to IIS to SQL Server; also a sort of delegation)
UPDATE
WIF seems to bring certain complexity, see Figure 7 and Figure 8 in Overview of the Claims Based Architecture which can be found on Windows Identity Foundation Simplifies User Access for Developers. And I have to admit that I do not know WIF. From what I see it might really be able to help you; or it might just "shift the problem"...
BUT from your comment
I have a security principal with the users AD account
I think you might be able to switch the identity of the user "running the web app" (i.e. making the calls to the database) using the mechanism that can also be used to roll your own "security principal-solution".
I am unsure if you then still need the impersonate
in Web.config
as suggested by @JustLoren. But it might well be.
You would start by replacing the current security principal with "your own" (the one you get through C2WTS
) in Global.asax
s Application_AuthenticateRequest
-method (hopefully this will still be called if you allow anonymous access?!?). But beware I would say this is by no means trivial.
You can find more information (and warnings) here:
UPDATE2
So.... You're saying that the answer
to my question may be to somehow
replace the current security principal
in this method?
That is what I meant, yes. Here comes some example code (excerpts of something without WIF I know to work).
Custom Principle Class (which you would not need):
using System;
using System.Collections.Generic;
using System.Web;
using System.Security.Principal;
namespace PrincipalTest
{
[Serializable]
public class MyPrincipal : WindowsPrincipal
{
public MyPrincipal(WindowsIdentity ntIdentity) : base(ntIdentity)
{
}
public bool IsAdmin
{
get
{
bool isAdmin = (string.Compare(this.Identity.Name,
@"<domain>\<user>", StringComparison.OrdinalIgnoreCase)
== 0);
return isAdmin;
}
}
}
}
In Global.asax
(you would do your WIF-magic here):
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
DotnetReportingPrincipal principal =
new MyPrincipal(HttpContext.Current.User.Identity
as WindowsIdentity);
// Attach the new principal object to the current HttpContext object
HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal
= System.Web.HttpContext.Current.User;
}
You can then do things like this (again not needed in your case I assume):
protected void Page_Load(object sender, EventArgs e)
{
IPrincipal current = HttpContext.Current.User;
if (current is MyPrincipal)
{
if (current as DotnetReportingPrincipal).IsAdmin)
{
...
}
}
}
In your situation I would try (hope :)) you can just set ASP.NET to use impersonation (again as suggested by @JustLoren). Hopefully IIS will then pass the "new" user down to SQL Server for querying.
If this is not the case, make sure your configuration allows for delegation. Here would be some hints:
Please remember that I am on very thin ice here. But - who knows...
You might be able to achieve something that has been shown in a Microsoft demo, not bad at all :)
Good luck!
Anotherupdate
Hmm - I am not sure it I like the way things go here ;) I honestly hope I did not point you in the wrong direction...
As you said, there is the WindowsClaimsIdentity
class:
there is a class in WIF that I'm not
familiar with called
WindowsClaimsIdentity which seems to
be a hybrid Windows/Claims Identity.
I'm trying to find how to set up my
impersonation using this class.
But it says:
If client is using Windows
authentication, create a
WindowsClaimsPrincipal. This principal
allows downcasting to WindowsPrincipal
and WindowsIdentity (to access things
like impersonation and other Windows
security specific features).
And your client is not using Windows authentication, I believe. So there will not be a WindowsPrincipal
; which is bad news.
For your scenario to work you absolutely need a WindowsPrincipal
in the end, I am quite convinced. This is because I believe this to be the only one you can pass to SQL Server. So the question is: how do you get a WindowsPrincipal
from "your cookie"?
The way you are doing thing now you might already get a WindowsPrincpal
by the way; but the wrong one, I fear. Why? Because you do not have <authentication mode="Windows" />
in your Web.config
. Or if you have it and allow anonymous access (which you do) you will end up with the NETWORK SERVICE
or machine account Windows Principal (you could easily check this and tell me if I am wrong though).
By the way: do you have any <authentication mode="...">
in Web.config
? I do not think so!?!
So again: how can you get a WindowsPrincipal
for the current user from the cookie information you have?
Here are my thoughts (I have to admit that they leave a bit of a foul smell in my mouth; it feels like reinventing the (SSO) wheel...).
- Configure your app to allow anonymous access in IIS (this is a requirement, as I understand).
- Set your app to use impersonation (in
Web.config
, see @JustLoren's answer)
- Use the code under "Obtain a Windows Token for the Original Caller" in How To: Use Protocol Transition and Constrained Delegation in ASP.NET 2.0 to obtain a
WindowsIdentity
in Global.asax
s Application_AuthenticateRequest
-method (using the username/password information you can hopefully retrieve from "the cookie system").
- Create a
WindowsPrincipal
from this WindowsIdentity
as described under "To create a WindowsPrincipal object for a single validation", step 2 in How to: Create a WindowsPrincipal Object ; essentially by doing WindowsPrincipal MyPrincipal = new WindowsPrincipal(MyIdentity);
.
- Attach you newly created
WindowsPrincipal
to the current HttpContext and thread (as proposed in "Update2").
- Cross your fingers :) But I have certain confidence in this. The shaky part seems to be to get the correct
WindowsIdentity
...
Yetanontherupdate
In section "Impersonate a Specific User in Code" of the article How to implement impersonation in an ASP.NET application still another way to get a WindowsIdentity
from username/password is used: LogonUserA
imported from advapi32.dll
.
Maybefinalupdate
After your latest update I can see clearer now. You are absolutely right with you question. As far as I know, changing (or taking influence on) what WindowsPrincipal.GetCurrent
returns is only possible by activating Windows Authentication and disabling anonymous access in IIS. Which is what you cannot do.
But wait!
Wow, this is getting dirty. But as I saw the tag "kludge" on your question I dare write the following... But beware: I am pretty sure this is not what Microsoft showed in this demo you mentioned :) Neither if this has any unwanted side effects!
What you should be able to do (my quick and dirty test showed it seems to work) is
- Add a
System.Security.Principal.WindowsImpersonationContext
field in Global.asax
- In
Global.asax
s Application_AuthenticateRequest
get a WindowsIdentity
for the user you want to impersonate (you seem to be able to do this as you could impersonate the user in code)
- Still in
Global.asax
s Application_AuthenticateRequest
impersonate the user (in code as you did before or as described in the article(s) cited above; e.g. here), do not call impersonationContext.Undo()
...
- In
Global.asax
s Application_EndRequest
call impersonationContext.Undo()
on the field you created two steps before
- Keep
<identity impersonate="true" />
in Web.config
- Cross all your fingers and toes :)
True, you would then still impersonate the user in code (i.e. "clutter your code with impersonation") but only slightly :) Meaning only in one place (Global.asax
).
What do you reckon?
@Rising Star: by the way: in your comment from 2010-05-31 14:03:31Z
you write "He says that you can uncheck that box and then...". Is this by intention or did you mean to write "check"? Because if you uncheck that box you have to check something else to still allow access to anyone. What would you (or he) check instead? Forms Authentication (as there is no "magic flute authentication" as you correctly noted here :))? Sorry, I (automatically) misread it as "check" until now...
Update (by Rising Star) I see you've converted this thread to community Wiki so I no longer have to manually update the question. This is going to be a mess, but once the problem is solved, we can clean it up a bit.
scherand asked how I attach a Windows Identity
to a cookie. This is done using the WIF framework; specifically, I do this by returning a WindowsClaimsPrincipal
from the ClaimsAuthenticationManager
as described in the "least privilege" link that I provided. Also, yes, the boss did say to uncheck the "anonymous access" box in the IIS settings; that seems to break everything.
I think you might be on the right track with your answer. I was just looking into doing my own custom code in global.asax myself.