role-based security and ActiveDirectory

S

SpaceMarine

hello,

im having a little problem w/ role-based security and ActiveDirectory
(AD), hoping someone can help. im trying to restrict access to my app
to only users within a particular AD group. details:

- ASP.NET 3.5 intranet app; Visual Studio 2008

- deployed to a Windows Server 2008 (IIS7) machine

- uses Windows authentication

- all desired domain users reside in a custom AD group, "FOO_BAR".

- requesting the User.Identity.Name yields: "OURDOMAIN\SomeUserName"

- requesting all group membership yields:

Everyone
OURDOMAIN\Domain Users
OURDOMAIN\FOO_BAR

....all looks good. so the problem? when i request:

User.IsInRole(@"OURDOMAIN\FOO_BAR") or
User.IsInRole("FOO_BAR") or

....i get False.

this is problematic because in my web.config im trying to restrict
access to the group-only:

<authorization>
<!-- Allow only group users -->
<allow roles="FOO_BAR"/>
<deny users="*"/>
<deny users="?"/>
</authorization>


any idea whats up? i read that ASP.NET's role-based security model
should be able to pick up a Windows-authenticated AD user's groups as
roles. is this not the case?


thanks!
sm


ps - here is how i get a loop of a user's group memberships...useful:

//convert user's groups to readable NT thang
IdentityReferenceCollection usersGroups = WindowsIdentity.GetCurrent
().Groups.Translate(System.Type.GetType
("System.Security.Principal.NTAccount"));

StringBuilder sb = new StringBuilder(200);

foreach (IdentityReference group in usersGroups)
sb.Append(group.Value + "<br/>");
 
G

Guest

any idea whats up? i read that ASP.NET's role-based security model
should be able to pick up a Windows-authenticated AD user's groups as
roles. is this not the case?

You need to use ADAM or similar
http://msdn.microsoft.com/en-us/library/ms998331.aspx

        //convert user's groups to readable NT thang
        IdentityReferenceCollection usersGroups = WindowsIdentity.GetCurrent
().Groups.Translate(System.Type.GetType
("System.Security.Principal.NTAccount"));

        StringBuilder sb = new StringBuilder(200);

        foreach (IdentityReference group in usersGroups)
                sb.Append(group.Value + "<br/>");

You can assign user to roles manually from the code.

In global.asax in Application_AuthenticateRequest you can use your
code from above as

string[] roles = new string[] { };

// your code here....

foreach (IdentityReference group in usersGroups)
roles.Add(group.Value);

//and then add our own custom principal to the request containing the
roles in the auth ticket
Context.User = new GenericPrincipal(Context.User.Identity, roles);
 
J

Joe Kaplan

This should work. It may be the case that the group in question either is
not security enabled and thus would not be the user's token or it has a
different account name than you think it does.

What you should do is verify what's actually in the token. Write some code
that translates the User.Identity object to a WindowsIdentity and then use
the Translate method on the IdentityReferenceCollection to translate to
NTAccount objects. Then you can dump out the names and see what's in there.

Alexey suggested using ADAM or creating a GenericPrincipal object but
neither of these are needed. I can't actually see why ADAM would help with
this scenario at all. The WindowsPrincipal should do exactly what you want
it to.

Joe K.
 
S

SpaceMarine

This should work.  It may be the case that the group in question either is
not security enabled and thus would not be the user's token or it has a
different account name than you think it does.

i see. if i ask our admins whether the group is "security enabled"
will that mean something to them, something they can check?


sm
 
J

Joe Kaplan

I hope so. :) It is radio button in the normal AD GUI. If the AD admins
don't know what you are talking about, I would lobby for qualified people to
take their positions.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
This should work. It may be the case that the group in question either is
not security enabled and thus would not be the user's token or it has a
different account name than you think it does.

i see. if i ask our admins whether the group is "security enabled"
will that mean something to them, something they can check?


sm
 
S

SpaceMarine

I hope so. :) It is radio button in the normal AD GUI.

ok it wasnt that :(. they sent me a screenshot -- Security was
selected, Distribution was not.

so it looks like this is next:
What you should do is verify what's actually in the token. Write some code
that translates the User.Identity object to a WindowsIdentity and then use
the Translate method on the IdentityReferenceCollection to translate to
NTAccount objects. Then you can dump out the names and see what's in there.

....is that something substainally different than my group looping
above?


thanks,
sm
 
S

SpaceMarine

You can assign user to roles manually from the code.

// your code here....

foreach (IdentityReference group in usersGroups)
roles.Add(group.Value);

//and then add our own custom principal to the request containing the
roles in the auth ticket
Context.User = new GenericPrincipal(Context.User.Identity, roles);

assuming i cant get this to work 100% out-of-the-box (users' AD Group
memberships equating to ASP.NET's Roles), you are right -- i could
always loop thru my above group collection and all each to the Roles
collection.

just seems kinda silly. :(


sm
 
J

Joe Kaplan

It should work fine. Normally a problem like this is that either:

- You have an incorrect name and the string match is failing as a result
- You have a domain local group from a domain in a different domain than
the web server
- You have a nested group and are still in Win2K mixed mode with AD
- The group isn't security enabled

You've already eliminated the last one and the other two seem less likely.
The idea behind looping through the groups is just for debug purposes to see
what's actually in the user's token. That will give you a better clue what's
going on and you can go from there.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
You can assign user to roles manually from the code.

// your code here....

foreach (IdentityReference group in usersGroups)
roles.Add(group.Value);

//and then add our own custom principal to the request containing the
roles in the auth ticket
Context.User = new GenericPrincipal(Context.User.Identity, roles);

assuming i cant get this to work 100% out-of-the-box (users' AD Group
memberships equating to ASP.NET's Roles), you are right -- i could
always loop thru my above group collection and all each to the Roles
collection.

just seems kinda silly. :(


sm
 
S

SpaceMarine

The idea behind looping through the groups is just for debug purposes to see
what's actually in the user's token. That will give you a better clue what's
going on and you can go from there.

ah...my initial thought was that by looping thru the current
WindowsIdentity's Groups and translating to NTAccounts as I am doing
(first post) was doing exactly that. but now i see the User.Identity
is NOT the same as the WindowsIdentity.GetCurrent(). (seems the
WindowsIdentity represents the thread running the ASP.NET code,
whereas User.Identity just represents the "client" identity. im still
learning the diffs!)

thus youre suggesting I "translate" the User.Identity to a
WindowsIdentity. so i did this:

WindowsIdentity winIdentity2 = (WindowsIdentity)User.Identity;

.....and re-created winIdentity2's groups collection, translating to
NTAccount. results -- group membership is *the same* as when i used:

WindowsIdentity.GetCurrent(true); //true = app is using
impersonation

....same exact groups when looped thru.


the plot thickens!!


btw i really appreciate your knowledge & help.
sm
 
S

SpaceMarine

It should work fine.  Normally a problem like this is that either:

 - You have an incorrect name and the string match is failing as a result
 - You have a domain local group from a domain in a different domain than
the web server
 - You have a nested group and are still in Win2K mixed mode with AD
 - The group isn't security enabled

You've already eliminated the last one and the other two seem less likely..

....yep, last is eliminated. first: i hope not, but that would be
easiest. ive tried w/ "FOO_BAR" (my group name, w/ an underscore in
it), as well as copying the full "OURDOMAIN\FOO_BAR" out of the group
collection -- i had to add the @ sign for csharp, so its @"OURDOMAIN
\FOO_BAR". both return False....

that leaves the middle two. im going to ask w/ my admin about them.

on #2 - while my group memberships say "OURDOMAIN\XXX", on the web
server the "My Computer->Properties" say "Domain: ourdomain.com". (the
server is hit internally via ("http://boxname.ourdomain.com")


sm
 
J

Joe Kaplan

Yes, this is well documented. WindowsIdentity.GetCurrent is only the same
as User.Identity when you have impersonation enabled. The thing that is
checked against when you use the <authorization> element in web.config or
when you do Context.User.IsInRole is the current authenticated user.

The idea behind this model is that ASP.NET has a generic mechanism for
representing authenticated user identity via Context.User (IPrincipal). Any
type of auth mechanism can plug into this by implementing IPrincipal.
Windows auth builds a WindowsPrincipal whereas Forms auth builds a
FormsPrincipal. You can also implement your own auth mechanisms that build
their own IPrincipal and IIdentity types.

The question is still whether or not Context.User.Identity belongs to the
group in question or not. If it does and the spelling is correct, then
Context.User.IsInRole *should* give you the behavior you want (as should
checks with the <authorization> element or PrincipalPermission.Demand
checks). It still isn't clear to me what the story is there from your
reply.

Since all code in Windows runs under the security context of a Windows
identity (represented by a security token), you can also determine what this
identity is at any time by doing WindowsIdentity.GetCurrent. This can
either be the process identity/token or an impersonated identity. However,
this identity only matters for Windows security checks made by the code. It
does not necessarily have anything to do with the authenticated user in a
web app. In cases where you are authenticating against something like a SQL
store using the membership provider, there IS no Windows user for that
person so it would not make sense for the code to be executing as that user
from the Windows perspective.

I hope that helps clarify things.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
The idea behind looping through the groups is just for debug purposes to
see
what's actually in the user's token. That will give you a better clue
what's
going on and you can go from there.

ah...my initial thought was that by looping thru the current
WindowsIdentity's Groups and translating to NTAccounts as I am doing
(first post) was doing exactly that. but now i see the User.Identity
is NOT the same as the WindowsIdentity.GetCurrent(). (seems the
WindowsIdentity represents the thread running the ASP.NET code,
whereas User.Identity just represents the "client" identity. im still
learning the diffs!)

thus youre suggesting I "translate" the User.Identity to a
WindowsIdentity. so i did this:

WindowsIdentity winIdentity2 = (WindowsIdentity)User.Identity;

.....and re-created winIdentity2's groups collection, translating to
NTAccount. results -- group membership is *the same* as when i used:

WindowsIdentity.GetCurrent(true); //true = app is using
impersonation

....same exact groups when looped thru.


the plot thickens!!


btw i really appreciate your knowledge & help.
sm
 
S

SpaceMarine

WindowsIdentity.GetCurrent is only the same as User.Identity when you have
impersonation enabled.

i see. i do have impersonation enabled -- enabling it and disabling
Anonymous Access seemed the only way to get ASP.NET apps to pickup
*anything* from AD. when these werent set my User.Identity.Name was
empty. by enabling impersonation and disabling anonymous
User.Identity.Name was filled w/ the currently-authenticated user.

The question is still whether or not Context.User.Identity belongs to the
group in question or not.  If it does and the spelling is correct, then
Context.User.IsInRole *should* give you the behavior you want (as should
checks with the <authorization> element or PrincipalPermission.Demand
checks).  It still isn't clear to me what the story is there from your
reply.

well, if my code is right, the answer is "Yes", the
Context.User.Identity does belong -- its translated group memberships
are exactly the same as the WindowsIdentity's groups. but it sounds
like this is expected as im using impersonation.

the code performing this check:

WindowsIdentity winIdentity2 = (WindowsIdentity)Context.User.Identity;

IdentityReferenceCollection usersGroups2 =
winIdentity2.Groups.Translate(System.Type.GetType
("System.Security.Principal.NTAccount"));

sb = new StringBuilder(200);

foreach (IdentityReference group in usersGroups2)
sb.Append(group.Value + "<br/>");

litGroups2.Text = sb.ToString();


.....there in the Literal i see my OURDOMAIN\FOO_BAR listed.


sm
 
S

SpaceMarine

It should work fine.  Normally a problem like this is that either:

 - You have an incorrect name and the string match is failing as a result
 - You have a domain local group from a domain in a different domain than
the web server
 - You have a nested group and are still in Win2K mixed mode with AD
 - The group isn't security enabled

on #2 and #3, my admin suggests those arent the case. he sent me a
screenshot that says:

Domain name:
ourdomain.com

Current domain functional level:
Windows Server 2003

....the "ourdomain.com" is the same as what my webserver reports. and
my take-away from the current-functional-level is that its not W2K
mode.

darn. this is puzzling and frustrating..


sm
 
J

Joe Kaplan

Ok, something weird is going on then.

If the translated group names include OURDOMAIN\FOO_BAR but the
Context.User.IsInRole(@"OURDOMAIN\FOO_BAR") fails, then something is very
wrong. Both things use very similar code paths.

The other two points don't matter given that the group is actually IN the
security token. The check based on the group name should work fine. The
debug step was just to ensure the spelling of the name was correct so there
was no typo.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
WindowsIdentity.GetCurrent is only the same as User.Identity when you have
impersonation enabled.

i see. i do have impersonation enabled -- enabling it and disabling
Anonymous Access seemed the only way to get ASP.NET apps to pickup
*anything* from AD. when these werent set my User.Identity.Name was
empty. by enabling impersonation and disabling anonymous
User.Identity.Name was filled w/ the currently-authenticated user.

The question is still whether or not Context.User.Identity belongs to the
group in question or not. If it does and the spelling is correct, then
Context.User.IsInRole *should* give you the behavior you want (as should
checks with the <authorization> element or PrincipalPermission.Demand
checks). It still isn't clear to me what the story is there from your
reply.

well, if my code is right, the answer is "Yes", the
Context.User.Identity does belong -- its translated group memberships
are exactly the same as the WindowsIdentity's groups. but it sounds
like this is expected as im using impersonation.

the code performing this check:

WindowsIdentity winIdentity2 = (WindowsIdentity)Context.User.Identity;

IdentityReferenceCollection usersGroups2 =
winIdentity2.Groups.Translate(System.Type.GetType
("System.Security.Principal.NTAccount"));

sb = new StringBuilder(200);

foreach (IdentityReference group in usersGroups2)
sb.Append(group.Value + "<br/>");

litGroups2.Text = sb.ToString();


.....there in the Literal i see my OURDOMAIN\FOO_BAR listed.


sm
 
S

SpaceMarine

Ok, something weird is going on then.

one thing to add -- would using the AzMan role manager muck anything
up? im using this for my app's custom roles. my assumption is i can
still use the AD roles for broad access limits, while using AzMan for
more granular custom access limits within the app:

<authentication mode="Windows" />

<authorization>
<!-- allow only group users.. not working :(
<allow roles="FOO_BAR"/>
<deny users="*"/>
<deny users="?"/> -->

<allow users="*" />
<deny users="?" />
</authorization>

<roleManager enabled="true"
defaultProvider="AzManRoleManager"
cacheRolesInCookie="true"
cookieName=".AppXRoles"
cookiePath="/"
cookieTimeout="30"
cookieRequireSSL="true"
cookieSlidingExpiration="true"
createPersistentCookie="false"
cookieProtection="All">
<providers>
<clear />
<add name="AzManRoleManager"
applicationName="AppX"
connectionStringName="azMan"
type="System.Web.Security.AuthorizationStoreRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
publicKeyToken=b03f5f7f11d50a3a" />
</providers>
</roleManager>
 
S

SpaceMarine

   User.IsInRole(@"OURDOMAIN\FOO_BAR") or
   User.IsInRole("FOO_BAR") or

on another forum someone suggested i had to use Roles.IsUserInRole(),
like so:

Response.Write("in role: " + Roles.IsUserInRole("FOO_BAR") + "<br/

....but this is False as well. argh.


sm
 
J

Joe Kaplan

The AzMan role provider would make a significant difference here since it
will change the roles the user has to the mapped roles in the AzMan model
and not the AD roles. If you remove that role provider, you should get
successful checks against the AD-based role using the fully qualified name.

If you want to use the AD role in conjunction with AzMan, you should map it
to an AzMan role and then use the mapped AzMan role for your authorization
checks.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
Ok, something weird is going on then.

one thing to add -- would using the AzMan role manager muck anything
up? im using this for my app's custom roles. my assumption is i can
still use the AD roles for broad access limits, while using AzMan for
more granular custom access limits within the app:

<authentication mode="Windows" />

<authorization>
<!-- allow only group users.. not working :(
<allow roles="FOO_BAR"/>
<deny users="*"/>
<deny users="?"/> -->

<allow users="*" />
<deny users="?" />
</authorization>

<roleManager enabled="true"
defaultProvider="AzManRoleManager"
cacheRolesInCookie="true"
cookieName=".AppXRoles"
cookiePath="/"
cookieTimeout="30"
cookieRequireSSL="true"
cookieSlidingExpiration="true"
createPersistentCookie="false"
cookieProtection="All">
<providers>
<clear />
<add name="AzManRoleManager"
applicationName="AppX"
connectionStringName="azMan"
type="System.Web.Security.AuthorizationStoreRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
publicKeyToken=b03f5f7f11d50a3a" />
</providers>
</roleManager>
 
S

SpaceMarine

The AzMan role provider would make a significant difference here since it
will change the roles the user has to the mapped roles in the AzMan model
and not the AD roles.

i see. so by using the AzMan provider i wipe out any intrinsic AD
roles from the windows token...

is that also the case w/ the SQL Role Provider? im using the AzMan
because my apps have custom roles that we cant ask our AD admins to
manage. i chose AzMan because 1) its lighter & simpler than a
database. 2) can use MMC snap-in to manage in a jiffy (i also have a
web UI but that can be done for any provider)... its been pretty
painless thus far.
 If you remove that role provider, you should get
successful checks against the AD-based role using the fully qualified name.

WE HAVE A WINNER!! that works. also, looks like i dont need the domain
name in the Role string, just the group name.
If you want to use the AD role in conjunction with AzMan, you should map it
to an AzMan role and then use the mapped AzMan role for your authorization
checks.

this works perfectly. THANK YOU, JOE!!!


this mystery has been solved. book 'em, boys...

sm
 
J

Joe Kaplan

Glad that works. To summarize, the role providers "overwrite" existing role
data, so if you want some sort of a "merged" view of roles, you may have to
do a little extra work to achieve that. In your case using AzMan, probably
the best thing to do is map the Windows group to an AzMan role in the AzMan
configuration and then let the AzMan framework handle the transformation of
that data on the fly.

In the long run this approach is probably better anway because then the
AD-specific stuff is not coded in your app at all but is externalized in in
the AzMan configuration. This adds a bit more flexibility to the whole
system.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
The AzMan role provider would make a significant difference here since it
will change the roles the user has to the mapped roles in the AzMan model
and not the AD roles.

i see. so by using the AzMan provider i wipe out any intrinsic AD
roles from the windows token...

is that also the case w/ the SQL Role Provider? im using the AzMan
because my apps have custom roles that we cant ask our AD admins to
manage. i chose AzMan because 1) its lighter & simpler than a
database. 2) can use MMC snap-in to manage in a jiffy (i also have a
web UI but that can be done for any provider)... its been pretty
painless thus far.
If you remove that role provider, you should get
successful checks against the AD-based role using the fully qualified
name.

WE HAVE A WINNER!! that works. also, looks like i dont need the domain
name in the Role string, just the group name.
If you want to use the AD role in conjunction with AzMan, you should map
it
to an AzMan role and then use the mapped AzMan role for your authorization
checks.

this works perfectly. THANK YOU, JOE!!!


this mystery has been solved. book 'em, boys...

sm
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top