c# LDAP membership enumeration - c#

here is my problem: I need to enumerate the members of certain groups in our Active Directory. The complication is that the DN of the objects that are in the 'membersOf' property of the groups do not contain the identifier I need. Specifically, the CN of the user object is useless to me, only a property of the user object (the userID) is useful.
So three approaches comes to mind:
I can first enumerate the group to get a list of DN's in the group, and then do a lookup on each user DN to find out their userID
I can enumerate every user in the AD, grabbing their userID & their membersOf collection, and then check afterwards if they have the right memberships.
I could grab a list of all users with their CN's and userID's, and then enumerate the groups to get the member CN's. Then I could join the lists on the original list to get my list of member userID's.
Some problems immediately appear - option 1) will generate an extremely large number of subqueries and congest network traffic (undesirable), and option 2) pulls a HUGE amount of data from AD (something like 30mb). Option 3) is middle of the road - but it still pulls quite a bit of data and has multiple queries. Is there another option for how to do this which does not have these problems?
I am doing this in c# using the System.DirectoryServices tools.
Thank you in advance for your time and consideration.

Maybe there's an option #4, too:
you could set up a DirectorySearcher which enumerates users
you could define memberOf=....... as one of your search criteria
you can define what attributes you need from the directory searcher very easily
If this works (and I'm under the impression I got this to work before - but it's been quite a while!) then you could do one, single fairly focused search and automatically get your information that you need.
Try something like this:
// define the "root" of your search (where to begin)
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://cn=users,dc=yourcompany,dc=com");
DirectorySearcher searcher = new DirectorySearcher(searchRoot);
// set properties
searcher.SearchScope = SearchScope.Subtree;
// define search filter
searcher.Filter = "(&(objectCategory=Person)(memberof=CN=YourGroup,OU=Users,DC=YourDomain,DC=com))";
// define attributes to load
searcher.PropertiesToLoad.Add("userid");
... add more if needed .....
foreach(SearchResult entry in searcher.FindAll())
{
string userid = entry.Properties["userid"][0].ToString();
}

Related

DirectorySearcher does not return all AD group members

I try to get all members of an Active Directory group in C#. My code looks like:
var searcher = new DirectorySearcher(defaultNamingContextEntry) {
Filter = "(memberOf=" + group.Properties["distinguishedName"].Value + ")",
PageSize = 1000,
SearchScope = SearchScope.Subtree,
Sort = new SortOption("sAMAccountName", SortDirection.Ascending)
};
var members = searcher.FindAll();
The code works so far, but has the problem that it does not return all group members.
I believe (I am not sure) that it returns what I can actually see for the "member" attribute in ADSI Edit (the group in question is a bit large to compare everything, but far below 1000), but these are not all members. There are users that are, according to AD Users & Computers, in the group which are not listed. If I look in the "Member Of" page, I can see the group, and ACL settings via the group affect the missing users, too. However, they are not in the result set.
The group in question is the primary group, in case this is relevant.
What is wrong here? What would be the correct parameters or query? Is this a problem of the AD itself?
Update: I have further investigated the problem (and tested Brian's solution, which I strongly recommend if your property collections are really large; I thought that there must be some kind of paging mechanism for properties, but could not find how it works):
I now know that the DirectorySearcher returns the correct result as seen in ADSI edit.
I also know that the "Primary Group" (set by the "Set Primary Group" button on the "Member Of" page in AD Users & Computers) is missing. Unix extensions are enabled in the AD, so we usually use this option. Changing the primary group changes which members are missing. The effect is deterministic.
I have looked into the entries using ADSI edit, but could not find the field that would allow me to add the primary group in my search. Although I have enabled everything (Mandatory, Optional, Constructed, Backlink and System-only attributes), I cannot see the primary group.
My revised question is: what is the correct LDAP filter for retrieving the primary group, too?
You need to do value ranging. See http://www.netid.washington.edu/documentation/enumeratingLargeGroups.aspx for an example.

LDAP query for user in AD is bringing back a computer object

I am using the following query in AD to bring back a user's mail address:
// get a DirectorySearcher object
DirectorySearcher search = new DirectorySearcher();
// specify the search filter
search.Filter = "(&(objectClass=user)(anr=" + login + "))";
// specify which property values to return in the search
search.PropertiesToLoad.Add("mail"); // smtp mail address
// perform the search
SearchResult result = search.FindOne();
if (result != null)
{
return result.Properties["mail"][0].ToString();
}
else
{
return null;
}
For a particular user who's login is "SRB" the query is bringing back a computer object called "SRB-PC".
I don't understand why as the filter says objectClass should be "user".
Also - why is it bringing it back as it were a "like" query - I want it to bring back only objects whose name exactly match the filter.
A Computer object is a sub Class of a User object in Active Directory. This is the reason why you find computers for your original search.
The objectCategory element is used to distinguish entities properly:-
as stated in the Object Class vs. Object Category page:
Prior to Windows Server 2008, the objectClass attribute is not indexed. This is because it has multiple values and is highly non-unique; that is, every instance of the objectClass attribute includes the top class. This means an index would be very large and ineffective. To locate objects of a given class, use the objectCategory attribute, which is single-valued and indexed. For more information about using these properties in search filters, see Deciding What to Find.
So it is far more efficient to search using the objectCategory instead of the objectClass.
OK - switched the filter to:
search.Filter = string.Format("(&(objectCategory=Person)(anr={0}))", login);
Don't really understand why it fixed it, but it did!
You will do well to use something like:
(&(objectCategory=person)(objectClass=user))
or
(sAMAccountType=805306368)
See some other MS queries.

Using LDAP/AD to find mailing lists user is subscribed to

I am currently looking into getting a list of mailing distribution lists that a user is subscribed to. I have never used Active Directories before, and after reading into various answers here and on MSDN, I'm very confused.
I have my query set up like this:
DirectorySearcher search = new DirectorySearcher();
search.Filter = String.Format("LDAP://CN={0},OU=<value here>,DC=<value>...", userName);
search.SearchScope = SearchScope.Subtree;
search.PropertiesToLoad.Add("memberOf");
SearchResult result = search.FindOne();
if(result != null)
{
// Do stuff here
}
Here are my questions:
What does the SearchRoot property within DirectorySearcher do? Do I need to set that up to have this query return a value?
Do I need to set the SearchScope value to be Subtree is I list only 1 OU as the base OU and have it search all sub OUs?
Is the string sent in to PropertiesToLoad.Add() generic, or does that need to be a string that is defined by my company?
Thanks for the advice!
(1) What does the SearchRoot property within DirectorySearcher do? Do I need to set that up to have this query return a value?
It defines the starting point of your search; compare it to a file system - it defines the starting directory from which you start your search. You'll be looking inside the SearchRoot and possibly you'll be looking at all its child containers. It's just used to reduce the possible number of containers to search for something
(2) Do I need to set the SearchScope value to be Subtree is I list only 1 OU as the base OU and have it search all sub OUs?
If you want to search OU or other container under your SearchRoot - then yes, you have to search the SearchScope.Subtree - otherwise the search will only look at your SearchRoot container itself (at all the objects like users, computers, groups inside that container - but not at sub-containers).
(3) Is the string sent in to PropertiesToLoad.Add() generic, or does that need to be a string that is defined by my company?
Those are LDAP attribute names - these can be both, the standard ones as well as possible custom extensions your company might have defined and installed. Here's a really nice list of all AD attributes as an Excel sheet (several, actually).

comparing multiple IEnumerables for distinct and different records

I am comparing the active directory groups of 3 or more users, and I will never know the number of users to compare before the program is run. What I am ultimately trying to do is to get a list of similar active directory groups between the users I am comparing and a list of the non-similar groups for the users. I also want to be able to tell what user is assigned to the non-similar groups which adds another issue in the comparison. Here is the code I have so far. Basically I foreach though this for every user but then I am stuck on not only how to compare them all, but how to store all of the IEnumerable objects before I have to compare them.
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, "Domain"))
{
using (PrincipalSearchResult<Principal> user = UserPrincipal.FindByIdentity(principalContext, userName))
{
return user.GetGroups();
}
}
Since I don't know the number of users, I don't how many Ienumerables I need to create to store the groups, much less compare them all.
Any help would be much appreciated. I have tried using a hashset, but I couldn't figure out how to retain the users properties along with the group assignments when I did this.

Using System.DirectoryServices.xxx is it possible to determine what AD Groups a user can manage?

I'm attempting to load a list of groups for a user and wanting to show if they have authority to edit group membership or not.
What in the Active Directory indicates that a user can edit the members of a group and how can i look this up using System.DirectoryServices in 3.5+
Im using the following to obtain the groups for a user
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, userName);
if ( user != null)
{
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
foreach(Principal p in groups)
{
if ( p is GroupPrincipal)
...
}
}
Any help appreciated
This is very time consuming, due to the way that permissions are managed on objects. A similar question might be, "How do I list every folder on the domain that an account can write data to". The reason this is time consuming is because each object holds it's own Access Control List (ACL).
I'm fairly sure the only way to find out every group you can manage would be to check every group and see what the permissions are on that group, then compare your group membership to the permissions on the group.
In Active Directory, how do I determine type of ActiveDirectoryAccessRule? has some code which may prove helpful if this is the route you end up going.
A saner approach might be to use the "Delegate" field to delegate permissions to certain groups, this field could be easily queried using LDAP, or let the person pick any group and then check the group's permissions after it's been selected.

Categories

Resources