Imagine i have structure
RootGroup <- Group{x} .... <- Group{x+n} <- Group100
How could I check that Group100
is member of RootGroup
I have this and it returns always false
private bool IsMemberOfInternal(string userOrGroupDistinguishedName, string groupMembershipDistinguishedName)
{
GroupPrincipal principal = null;
GroupPrincipal target = null;
try
{
principal = _getUserGroupPrincipalFunc(principalContext, userOrGroupDistinguishedName);
target = _getUserGroupPrincipalFunc(principalContext, groupMembershipDistinguishedName);
if (principal != default(GroupPrincipal)
&& target != default(GroupPrincipal))
{
return principal.IsMemberOf(target);
}
}
catch
{
}
return false;
}
You're better off not using GroupPrincipal
for this. AD actually has a built-in way to do this kind of search that is far faster than any thing GroupPrincipal
can do. You can use that by using DirectoryEntry
and DirectorySearcher
directly (that's what GroupPrincipal
and PrincipalSearcher
use behind the scenes anyway).
I wrote an article about figuring out if a user is a member of a specific group, but it applies just the same to groups. I have a sample method there that you can use for this:
private static bool IsUserInGroup(DirectoryEntry user, DirectoryEntry group, bool recursive) {
//fetch the attributes we're going to need
user.RefreshCache(new [] {"distinguishedName", "objectSid"});
group.RefreshCache(new [] {"distinguishedName", "groupType"});
//This magic number tells AD to look for the user recursively through any nested groups
var recursiveFilter = recursive ? ":1.2.840.113556.1.4.1941:" : "";
var userDn = (string) user.Properties["distinguishedName"].Value;
var groupDn = (string) group.Properties["distinguishedName"].Value;
var filter = $"(member{recursiveFilter}={userDn})";
if (((int) group.Properties["groupType"].Value & 8) == 0) {
var groupDomainDn = groupDn.Substring(
groupDn.IndexOf(",DC=", StringComparison.Ordinal));
var userDomainDn = userDn.Substring(
userDn.IndexOf(",DC=", StringComparison.Ordinal));
if (groupDomainDn != userDomainDn) {
//It's a Domain Local group, and the user and group are on
//different domains, so the account might show up as a Foreign
//Security Principal. So construct a list of SID's that could
//appear in the group for this user
var fspFilters = new StringBuilder();
var userSid =
new SecurityIdentifier((byte[]) user.Properties["objectSid"].Value, 0);
fspFilters.Append(
$"(member{recursiveFilter}=CN={userSid},CN=ForeignSecurityPrincipals{groupDomainDn})");
if (recursive) {
//Any of the groups the user is in could show up as an FSP,
//so we need to check for them all
user.RefreshCache(new [] {"tokenGroupsGlobalAndUniversal"});
var tokenGroups = user.Properties["tokenGroupsGlobalAndUniversal"];
foreach (byte[] token in tokenGroups) {
var groupSid = new SecurityIdentifier(token, 0);
fspFilters.Append(
$"(member{recursiveFilter}=CN={groupSid},CN=ForeignSecurityPrincipals{groupDomainDn})");
}
}
filter = $"(|{filter}{fspFilters})";
}
}
var searcher = new DirectorySearcher {
Filter = filter,
SearchRoot = group,
PageSize = 1, //we're only looking for one object
SearchScope = SearchScope.Base
};
searcher.PropertiesToLoad.Add("cn"); //just so it doesn't load every property
return searcher.FindOne() != null;
}
This method also handles the case where the user
(or your child group) is on an external trusted domain from the root group. That may or may not be a thing you have to worry about.
Just pass a DirectoryEntry
for your Group100
as the user
parameter. Something like this:
var isMemberOf = IsUserInGroup(
new DirectoryEntry($"LDAP://{userOrGroupDistinguishedName}"),
new DirectoryEntry($"LDAP://{groupMembershipDistinguishedName}"),
true);
For recursive searches (when you pass true
for the recursive
parameter), it uses the LDAP_MATCHING_RULE_IN_CHAIN
"matching rule OID" (as explained here):
This rule is limited to filters that apply to the DN. This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.