Different results for Active Directory "memberof" on different computers - c#

I have some code that retrieves the Active Directory groups that a user is a member of. On localhost it returns the correct results, but when deployed to a another computer (web server on same network) it returns much less results.
I am specifying AD server and a special user name and password I was given by administrators to access.
DirectoryEntry de = new DirectoryEntry("LDAP://***:389", "***", "***");
DirectorySearcher ds = new DirectorySearcher(de);
ds.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + search + "))";
ds.SearchScope = SearchScope.Subtree;
ds.PropertiesToLoad.Add("*");
SearchResult rs = ds.FindOne();
if (rs != null)
{
if (rs.GetDirectoryEntry().Properties["memberof"].Value != null)
//rest of code removed
I also tried a different method and the results were also different...
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "***, "***", "***"))
{
UserPrincipal user = UserPrincipal.FindByIdentity(pc, name);
if (user != null)
{
List<string> groups = new List<string>();
PrincipalSearchResult<Principal> groups2 = user.GetAuthorizationGroups();
//rest of code removed
I would have thought that by specifying a user name and password that the result should be the same. Any idea why this is happening?

These are different because they're retrieving different datasets. The memberOf attribute is constructed on the fly and it will give you the groups the user is a /direct/ member of. The GetAuthorizationGroups() call, on the other hand, will give you all the security groups that the user is transitively a member of. It does this by looking at the tokenGroups attribute in AD.

Related

DirectorySearcher object data retrieving not working on Azure hosted application

This code used to work for me in order to retrieve the AD information of a user when passing ID by parameter.
public UsersDTO GetUserFromActiveDirectoryByID(string userID)
{
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "(&(objectClass=user)(objectcategory=person)(name=" + userID + "))";
SearchResultCollection results = ds.FindAll();
SearchResult userProperty = results[0];
UsersDTO user = new UsersDTO();
if (userProperty.Properties["mail"].Count > 0)
{
user.fullName = userProperty.Properties["displayname"][0].ToString();
user.email = userProperty.Properties["mail"][0].ToString();
}
return user;
}
It worked while the application service was hosted in another server, but now that it has been migrated to Azure, the FindAll command (also FindOne was tested) returns "There was an error retrieving the data.","Status":400,"Detail":"Access is denied."
You aren't setting the SearchRoot of your DirectorySearcher. The documentation for SearchRoot says:
If SearchRoot is a null reference (Nothing in Visual Basic), the search root is set to the root of the domain that your server is currently using.
If the other server was joined to the domain that you are trying to search, then that's why it was working. But that is no longer true when you're on Azure.
So you need to specify the SearchRoot to point it at your domain:
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = new DirectoryEntry("LDAP://example.com");
This may also introduce issue of whether you can actually access your domain controllers from Azure. You may need to open firewall rules to allow it, depending on how your environment is setup.

How to get users from Active Directory in C# and display them in a ComboBox

I'm trying to show, in a ComboBox control, the users from an Active Directory on the network. To do this, I've the next function:
public static List<Usuario> MostrarUsuariosDominio()
{
List<Usuario> rst = new List<Usuario>();
try
{
DirectoryContext dc = new DirectoryContext(DirectoryContextType.Domain, Environment.UserDomainName);
Domain domain = Domain.GetDomain(dc);
DirectoryEntry de = domain.GetDirectoryEntry();
DirectorySearcher adSearcher = new DirectorySearcher(de);
adSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
adSearcher.PropertiesToLoad.Add("samaccountname");
SearchResult result;
SearchResultCollection iResult = adSearcher.FindAll();
Usuario item;
if (iResult != null)
{
for (int counter = 0; counter < iResult.Count; counter++)
{
result = iResult[counter];
if (result.Properties.Contains("samaccountname"))
{
item = new Usuario();
item.Nombre = (String)result.Properties["samaccountname"][0];
rst.Add(item);
}
}
}
adSearcher.Dispose();
}
catch (Exception ex)
{
Usuario item = new Usuario();
item.Nombre = "No se pudo recuperar la lista de usuarios";
rst.Add(item);
}
return rst;
}
If I run the application in the PC who's domain controller it works fine: the function returns to me all users. But if I run it on another PC, I get the exception:
Specified domain does not exist or couldn't contact with it
Is there any way to recover the users list from another PC?
This line:
DirectoryContext dc = new DirectoryContext(DirectoryContextType.Domain, Environment.UserDomainName);
Tells it to connect to the domain that the current user is logged into. Are you logged in as a domain user?
Maybe check what Environment.UserDomainName is equal to and see if it is right.
If it's right, then it may be a network issue - it can't talk to the domain. Do you need to be connected to VPN?
To get all the users in a Active Directory domain you can use an DirectorySearcher class object to querie to a domain about all the users availables in that domain.
The class DirectorySearcher is contained in System.DirectoryServices namespace and is a class to perform queries against Active Directory Domain Services.
In this page is an example about how to do it:
...
string DomainPath = "LDAP://DC=xxxx,DC=com"
DirectoryEntry searchRoot = new DirectoryEntry(DomainPath);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(objectClass=user)(objectCategory=person))";
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("usergroup");
search.PropertiesToLoad.Add("displayname");//first name
SearchResultCollection resultCol = search.FindAll();
...
In DirectorySearcher, create a DirectorySearcher object which searches
for all users in a domain. search.Filter = "(&(objectClass=user)(objectCategory=person))" filters the search.
The search filter syntax looks a bit complicated, but basically it
filters the search results to only include users -> "objectCategory=person" and "objectClass=user" - and excludes disabled
user accounts by performing a bitwise AND of the userAccountControl
flags and the "account disabled" flag.
To point to the local Active Directory domain you can use this:
DirectoryEntry searchRoot = new DirectoryEntry("WinNT://" + Environment.MachineName);
You can combine that example with this code that uses foreach instead of use the for loop in the page example:
foreach (SearchResult result in resultCol)
{
yourComboBox.Items.Add(result.Properties["displayname"]);
}
I left here some pages from the Microsoft MSDN and codeproject.com sites:
DirectorySearcher Class
Get List of Active Directory Users in C#
SearchResultCollection Class
SearchResult Class
SearchResult.Properties Property

Connect to Active Directory via LDAP

I want to connect to our local Active Directory with C#.
I've found this good documentation.
But I really don't get how to connect via LDAP.
Can somebody of you explain how to use the asked parameters?
Sample Code:
static DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
DirectoryEntry ldapConnection = new DirectoryEntry("rizzo.leeds-art.ac.uk");
ldapConnection.Path = "LDAP://OU=staffusers,DC=leeds-art,DC=ac,DC=uk";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
I just have the Hostname and the IP Address of our Active Directory Server. What does DC=xxx,DC=xx and so on mean?
DC is your domain. If you want to connect to the domain example.com than your dc's are: DC=example,DC=com
You actually don't need any hostname or ip address of your domain controller (There could be plenty of them).
Just imagine that you're connecting to the domain itself. So for connecting to the domain example.com you can simply write
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
And you're done.
You can also specify a user and a password used to connect:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com", "username", "password");
Also be sure to always write LDAP in upper case. I had some trouble and strange exceptions until I read somewhere that I should try to write it in upper case and that solved my problems.
The directoryEntry.Path Property allows you to dive deeper into your domain. So if you want to search a user in a specific OU (Organizational Unit) you can set it there.
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
directoryEntry.Path = "LDAP://OU=Specific Users,OU=All Users,OU=Users,DC=example,DC=com";
This would match the following AD hierarchy:
com
example
Users
All Users
Specific Users
Simply write the hierarchy from deepest to highest.
Now you can do plenty of things
For example search a user by account name and get the user's surname:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://example.com");
DirectorySearcher searcher = new DirectorySearcher(directoryEntry) {
PageSize = int.MaxValue,
Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=AnAccountName))"
};
searcher.PropertiesToLoad.Add("sn");
var result = searcher.FindOne();
if (result == null) {
return; // Or whatever you need to do in this case
}
string surname;
if (result.Properties.Contains("sn")) {
surname = result.Properties["sn"][0].ToString();
}
ldapConnection is the server adres: ldap.example.com
Ldap.Connection.Path is the path inside the ADS that you like to use insert in LDAP format.
OU=Your_OU,OU=other_ou,dc=example,dc=com
You start at the deepest OU working back to the root of the AD, then add dc=X for every domain section until you have everything including the top level domain
Now i miss a parameter to authenticate, this works the same as the path for the username
CN=username,OU=users,DC=example,DC=com
Introduction to LDAP
If your email address is 'myname#mydomain.com', try changing the createDirectoryEntry() as below.
XYZ is an optional parameter if it exists in mydomain directory
static DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
DirectoryEntry ldapConnection = new DirectoryEntry("myname.mydomain.com");
ldapConnection.Path = "LDAP://OU=Users, OU=XYZ,DC=mydomain,DC=com";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
This will basically check for com -> mydomain -> XYZ -> Users -> abcd
The main function looks as below:
try
{
username = "Firstname LastName"
DirectoryEntry myLdapConnection = createDirectoryEntry();
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(cn=" + username + ")";
....

How to find an user on a LDAP server

When trying to find an User on a LDAP Server, I get the following error "Unknown error (0x8000500c)"
This is the code I'm using:
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "gandalf.intrafg");
UserPrincipal p = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, "Consultor1");
Indidentally, the following piece of code seems to work (no exception is generated), but the samAccountName comes through as a byte array. Anybody knows why?
DirectoryEntry entry = new DirectoryEntry("LDAP://gandalf.intrafg");
DirectorySearcher searcher = new DirectorySearcher(entry);
//searcher.PropertiesToLoad.Add("givenName");
//searcher.PropertiesToLoad.Add("sn");
searcher.PropertiesToLoad.Add("samAccountName");
searcher.Filter = "(&(objectCategory=person)(samAccountName=Consultor1))";
SearchResult result = searcher.FindOne();
Your second code block works just fine, I however did not pass the domain name in the DirectoryEntry initializer.
Directory entry = new DirectoryEntry();
//other code
result.Properties["samAccountName"][0].ToString();
The code you have should be fine - it works for me, no problem at all.
However: you're not telling us what you fill in for domain_name here:
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "domain_name");
or userId here:
UserPrincipal p = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, UserId);
The domain_name must be in the "old" NetBIOS style, e.g. FABRIKAM - no DNS-style like fabrikam.com or AD-style like dc=fabrikom,dc=com or even a full LDAP path.
The userId must be a valid SAM account name, e.g. max. of 20 chars, letters and numerics only (except for a few valid special chars).
Are you complying with these requirements??

Directory Services, Search all available providers

I have the following method used for searching for a User Group either on the local computer (done first) or in the Current Forest.
public string FindUserGroup(string group)
{
//Search local computer
using (DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
//Search current forest
Forest forest = Forest.GetCurrentForest();
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
}
return string.Empty;
}
My problem is that we as an example have say "domain.local" and "mydomain.local", and my current login is bound to "domain.local", then using below won't be able to find anything in "mydomain.local", even if I through the Windows User Interface is able to.
How can I search all viewable providers from my computers perspective when I don't nessesarily know them all? Do I REALLY have to do the Registry Work my self?
Edit:
One difference in the 2 domains is the "level" they are on when I in an object browser dialog chooses "Locations", it layouts as:
Computer
Entire Direction
domain.local
mydomain.local
So "mydomain.local" excists outside what is referred to as "Entire Directory", yet my computer can locate it, if that makes any difference?
I don't see a problem as this code here would have already be binded to the other domains.
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
Are you trying to say that later on you're binding a DirectoryEntry on your own, and you can't find objects from other domain?

Categories