I'm writing an MVC-based (.NET 4.0) website that requires login credentials from my corporate LDAP server. What my code requires is to allow only the users that are part of a certain group. As an example, I could be looking for users that are part of the "Corporate IT" group. My credentials could be part of the "System Admins" group which is a subgroup of "Corporate IT". I'm using Forms Authentication.
How would I recursively check what group a user is under when they log in?
For anybody else coming here from a search for this type of query, here is how I did it in my application:
The key is 1.2.840.113556.1.4.1941 extended search filter. Since this particular filter works with DNs only, I first get hold of DN of the user I want to check and then query groups to see if this particular user is a member of any of groups in chain.
internal const string UserNameSearchFilter = "(&(objectCategory=user)(objectClass=user)(|(userPrincipalName={0})(samAccountName={0})))";
internal const string MembershipFilter = "(&(objectCategory=group)(objectClass=group)(cn=MyGroup)(member:1.2.840.113556.1.4.1941:={0}))";
using (var de = new DirectoryEntry(AppSettings.LDAPRootContainer, AppSettings.AdminUser, AppSettings.AdminPassword, AuthenticationTypes.FastBind))
using (var ds = new DirectorySearcher(de) { Filter = string.Format(UserNameSearchFilter, username) })
{
ds.PropertiesToLoad.AddRange(new[] { "distinguishedName" });
var user = ds.FindOne();
if (user != null)
using (var gds = new DirectorySearcher(de) { PropertyNamesOnly = true, Filter = string.Format(MembershipFilter, user.Properties["distinguishedName"][0] as string) })
{
gds.PropertiesToLoad.AddRange(new[] { "objectGuid" });
return gds.FindOne() != null;
}
}
I found that using the GroupPrincipal.GetMembers
with the recursive flag was fast and effective.
public bool IsMember(string groupName, string samAccountName)
{
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, samAccountName))
using(GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName))
{
return group.GetMembers(true).OfType<Principal>().Any(u => u.SamAccountName.Equals(user.SamAccountName, StringComparison.InvariantCultureIgnoreCase));
}
}
If you want to check for the membership of a specific user, bind to the AD object in question and retrieve the tokenGroups attribute. It contains all direct and indirect group memberships in binary form - it's an array of byte arrays. Each byte array can be passed ot the constructor of the SecurityIdentifier class and subsequently being converted to an NTAccount which contains the name of the group in cleartext.
var sids = new IdentityReferenceCollection();
foreach (byte[] group in tokenGroups)
{
sids.Add(new SecurityIdentifier(group, 0));
}
var accounts = sids.Translate(typeof(NTAccount));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With