Thursday, May 22, 2008

Querying Active Directory groups via LDAP in ASP.NET

The RoleManager in .NET is great for SQL-based auth, but is really lacking when it comes to Windows auth. I needed to get a list of all users in a role, and was excited to find that the following method exists:

Roles.GetUsersInRole("RoleName");
By the way, to use RoleManager, add the following code in your Web.Config file within the System.Web tags:


<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider" />
Perfect, except that you get the following error when using it with Windows auth:
"The configured Role Provider (WindowsTokenRoleProvider) relies upon Windows authentication to determine the groups that the user is allowed to be a member of. ASP.NET Role Manager cannot be used to manage Windows users and groups. Please use the SQLRoleProvider if you would like to support custom user/role assignment."
Rather lame. I don't want to add/edit/delete users and groups - just look up what users are in a group. In any case, I ended up querying this info from Active Directory with LDAP. I made a static class called "AuthUtils" and first added the following method:


using System.Collections.Generic;
using System.DirectoryServices;
...

public static List<string> GetUsersInGroup(string LDAPAddress) {
List<string> groupMembers = new List();
DirectoryEntry group = new DirectoryEntry(LDAPAddress);
foreach(object dn in group.Properties["member"]) {
string[] parsedName = dn.ToString().Replace("CN=", String.Empty).Split(',');
groupMembers.Add(parsedName[0]);
}

return groupMembers;
}
which is callable with the following:


string LDAPGroupAddress = "LDAP://domain.com/CN=GroupName,CN=Users,DC=domain,DC=com";
List<string> groupMembers = AuthUtils.GetUsersInGroup(LDAPGroupAddress);
The above method strips out the CN (Canonical Name) tags from the results, and returns a list of user name strings.

I also built another quick method in AuthUtils to determine if a user is in a role, which also depends on the "GetUsersInGroup" method that was just built:


public static bool IsInRole(string LDAPAddress, string user) {
List<string> groupMembers = GetUsersInGroup(LDAPAddress);
return groupMembers.Contains(user);
}
This simply gets the group members as above, and uses the handy "Contains" method that's available in generic lists to return a true or false.

There is a User.IsInRole that works with regular NTLM auth, but for some reason it seems a bit flaky with an Active Directory back end. The method I've created above has always worked for me so far. I'm in the process of figuring out what the issue is with the regular User.IsInRole("RoleName") method, as it's bothersome that this doesn't work consistently.

UPDATE: Roles.IsUserInRole("RoleName") works fine with AD (vs. using User.IsInRole("RoleName"), as long as you're using IIS. It doesn't seem to work well with Visual Studio's built-in Web server (it always caches the role info).

No comments: