Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursively querying LDAP group membership

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?

like image 844
MarkP Avatar asked Oct 19 '11 19:10

MarkP


3 Answers

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;
        }
}
like image 120
Maverik Avatar answered Oct 13 '22 20:10

Maverik


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));
    }

}
like image 5
Katelyn Rochat Avatar answered Oct 13 '22 20:10

Katelyn Rochat


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));
like image 1
Henning Krause Avatar answered Oct 13 '22 22:10

Henning Krause