How do you do a query of an LDAP store by sAMAccountName and Domain? What is the "domain" property named in Active Directory or LDAP terms?
This is what I have for the filter so far. I'd like to be able to add in the domain:
(&(objectCategory=Person)(sAMAccountName=BTYNDALL))
This question is related to
active-directory
ldap
ldap-query
First, modify your search filter to only look for users and not contacts:
(&(objectCategory=person)(objectClass=user)(sAMAccountName=BTYNDALL))
You can enumerate all of the domains of a forest by connecting to the configuration partition and enumerating all the entries in the partitions container. Sorry I don't have any C# code right now but here is some vbscript code I've used in the past:
Set objRootDSE = GetObject("LDAP://RootDSE")
AdComm.Properties("Sort on") = "name"
AdComm.CommandText = "<LDAP://cn=Partitions," & _
objRootDSE.Get("ConfigurationNamingContext") & ">;" & _
"(&(objectcategory=crossRef)(systemFlags=3));" & _
"name,nCName,dnsRoot;onelevel"
set AdRs = AdComm.Execute
From that you can retrieve the name and dnsRoot of each partition:
AdRs.MoveFirst
With AdRs
While Not .EOF
dnsRoot = .Fields("dnsRoot")
Set objOption = Document.createElement("OPTION")
objOption.Text = dnsRoot(0)
objOption.Value = "LDAP://" & dnsRoot(0) & "/" & .Fields("nCName").Value
Domain.Add(objOption)
.MoveNext
Wend
End With
You have to perform your search in the domain:
http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx So, basically your should bind to a domain in order to search inside this domain.
You can use following queries
Users whose Logon Name(Pre-Windows 2000) is equal to John
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(sAMAccountName=**John**))
All Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370))
Enabled Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(!userAccountControl:1.2.840.113556.1.4.803:=2))
Disabled Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(userAccountControl:1.2.840.113556.1.4.803:=2))
LockedOut Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(lockouttime>=1))
"Domain" is not a property of an LDAP object. It is more like the name of the database the object is stored in.
So you have to connect to the right database (in LDAP terms: "bind to the domain/directory server") in order to perform a search in that database.
Once you bound successfully, your query in it's current shape is all you need.
BTW: Choosing "ObjectCategory=Person"
over "ObjectClass=user"
was a good decision. In AD, the former is an "indexed property" with excellent performance, the latter is not indexed and a tad slower.
You have to perform your search in the domain:
http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx So, basically your should bind to a domain in order to search inside this domain.
I have written a C# class incorporating
However, it is not Site-aware.
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
public static class ADUserFinder
{
private static Dictionary<string, string> _dictDomain2LDAPPath;
private static Dictionary<string, string> DictDomain2LDAPPath
{
get
{
if (null == _dictDomain2LDAPPath)
{
string configContainer;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"))
configContainer = rootDSE.Properties["ConfigurationNamingContext"].Value.ToString();
using (DirectoryEntry partitionsContainer = new DirectoryEntry("LDAP://CN=Partitions," + configContainer))
using (DirectorySearcher dsPartitions = new DirectorySearcher(
partitionsContainer,
"(&(objectcategory=crossRef)(systemFlags=3))",
new string[] { "name", "nCName", "dnsRoot" },
SearchScope.OneLevel
))
using (SearchResultCollection srcPartitions = dsPartitions.FindAll())
{
_dictDomain2LDAPPath = srcPartitions.OfType<SearchResult>()
.ToDictionary(
result => result.Properties["name"][0].ToString(), // the DOMAIN part
result => $"LDAP://{result.Properties["dnsRoot"][0]}/{result.Properties["nCName"][0]}"
);
}
}
return _dictDomain2LDAPPath;
}
}
private static DirectoryEntry FindRootEntry(string domainPart)
{
if (DictDomain2LDAPPath.ContainsKey(domainPart))
return new DirectoryEntry(DictDomain2LDAPPath[domainPart]);
else
throw new ArgumentException($"Domain \"{domainPart}\" is unknown in Active Directory");
}
public static DirectoryEntry FindUser(string domain, string sAMAccountName)
{
using (DirectoryEntry rootEntryForDomain = FindRootEntry(domain))
using (DirectorySearcher dsUser = new DirectorySearcher(
rootEntryForDomain,
$"(&(sAMAccountType=805306368)(sAMAccountName={EscapeLdapSearchFilter(sAMAccountName)}))" // magic number 805306368 means "user objects", it's more efficient than (objectClass=user)
))
return dsUser.FindOne().GetDirectoryEntry();
}
public static DirectoryEntry FindUser(string domainBackslashSAMAccountName)
{
string[] domainAndsAMAccountName = domainBackslashSAMAccountName.Split('\\');
if (domainAndsAMAccountName.Length != 2)
throw new ArgumentException($"User name \"{domainBackslashSAMAccountName}\" is not in correct format DOMAIN\\SAMACCOUNTNAME", "DomainBackslashSAMAccountName");
string domain = domainAndsAMAccountName[0];
string sAMAccountName = domainAndsAMAccountName[1];
return FindUser(domain, sAMAccountName);
}
/// <summary>
/// Escapes the LDAP search filter to prevent LDAP injection attacks.
/// Copied from https://stackoverflow.com/questions/649149/how-to-escape-a-string-in-c-for-use-in-an-ldap-query
/// </summary>
/// <param name="searchFilter">The search filter.</param>
/// <see cref="https://blogs.oracle.com/shankar/entry/what_is_ldap_injection" />
/// <see cref="http://msdn.microsoft.com/en-us/library/aa746475.aspx" />
/// <returns>The escaped search filter.</returns>
private static string EscapeLdapSearchFilter(string searchFilter)
{
StringBuilder escape = new StringBuilder();
for (int i = 0; i < searchFilter.Length; ++i)
{
char current = searchFilter[i];
switch (current)
{
case '\\':
escape.Append(@"\5c");
break;
case '*':
escape.Append(@"\2a");
break;
case '(':
escape.Append(@"\28");
break;
case ')':
escape.Append(@"\29");
break;
case '\u0000':
escape.Append(@"\00");
break;
case '/':
escape.Append(@"\2f");
break;
default:
escape.Append(current);
break;
}
}
return escape.ToString();
}
}
"Domain" is not a property of an LDAP object. It is more like the name of the database the object is stored in.
So you have to connect to the right database (in LDAP terms: "bind to the domain/directory server") in order to perform a search in that database.
Once you bound successfully, your query in it's current shape is all you need.
BTW: Choosing "ObjectCategory=Person"
over "ObjectClass=user"
was a good decision. In AD, the former is an "indexed property" with excellent performance, the latter is not indexed and a tad slower.
I have written a C# class incorporating
However, it is not Site-aware.
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
public static class ADUserFinder
{
private static Dictionary<string, string> _dictDomain2LDAPPath;
private static Dictionary<string, string> DictDomain2LDAPPath
{
get
{
if (null == _dictDomain2LDAPPath)
{
string configContainer;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"))
configContainer = rootDSE.Properties["ConfigurationNamingContext"].Value.ToString();
using (DirectoryEntry partitionsContainer = new DirectoryEntry("LDAP://CN=Partitions," + configContainer))
using (DirectorySearcher dsPartitions = new DirectorySearcher(
partitionsContainer,
"(&(objectcategory=crossRef)(systemFlags=3))",
new string[] { "name", "nCName", "dnsRoot" },
SearchScope.OneLevel
))
using (SearchResultCollection srcPartitions = dsPartitions.FindAll())
{
_dictDomain2LDAPPath = srcPartitions.OfType<SearchResult>()
.ToDictionary(
result => result.Properties["name"][0].ToString(), // the DOMAIN part
result => $"LDAP://{result.Properties["dnsRoot"][0]}/{result.Properties["nCName"][0]}"
);
}
}
return _dictDomain2LDAPPath;
}
}
private static DirectoryEntry FindRootEntry(string domainPart)
{
if (DictDomain2LDAPPath.ContainsKey(domainPart))
return new DirectoryEntry(DictDomain2LDAPPath[domainPart]);
else
throw new ArgumentException($"Domain \"{domainPart}\" is unknown in Active Directory");
}
public static DirectoryEntry FindUser(string domain, string sAMAccountName)
{
using (DirectoryEntry rootEntryForDomain = FindRootEntry(domain))
using (DirectorySearcher dsUser = new DirectorySearcher(
rootEntryForDomain,
$"(&(sAMAccountType=805306368)(sAMAccountName={EscapeLdapSearchFilter(sAMAccountName)}))" // magic number 805306368 means "user objects", it's more efficient than (objectClass=user)
))
return dsUser.FindOne().GetDirectoryEntry();
}
public static DirectoryEntry FindUser(string domainBackslashSAMAccountName)
{
string[] domainAndsAMAccountName = domainBackslashSAMAccountName.Split('\\');
if (domainAndsAMAccountName.Length != 2)
throw new ArgumentException($"User name \"{domainBackslashSAMAccountName}\" is not in correct format DOMAIN\\SAMACCOUNTNAME", "DomainBackslashSAMAccountName");
string domain = domainAndsAMAccountName[0];
string sAMAccountName = domainAndsAMAccountName[1];
return FindUser(domain, sAMAccountName);
}
/// <summary>
/// Escapes the LDAP search filter to prevent LDAP injection attacks.
/// Copied from https://stackoverflow.com/questions/649149/how-to-escape-a-string-in-c-for-use-in-an-ldap-query
/// </summary>
/// <param name="searchFilter">The search filter.</param>
/// <see cref="https://blogs.oracle.com/shankar/entry/what_is_ldap_injection" />
/// <see cref="http://msdn.microsoft.com/en-us/library/aa746475.aspx" />
/// <returns>The escaped search filter.</returns>
private static string EscapeLdapSearchFilter(string searchFilter)
{
StringBuilder escape = new StringBuilder();
for (int i = 0; i < searchFilter.Length; ++i)
{
char current = searchFilter[i];
switch (current)
{
case '\\':
escape.Append(@"\5c");
break;
case '*':
escape.Append(@"\2a");
break;
case '(':
escape.Append(@"\28");
break;
case ')':
escape.Append(@"\29");
break;
case '\u0000':
escape.Append(@"\00");
break;
case '/':
escape.Append(@"\2f");
break;
default:
escape.Append(current);
break;
}
}
return escape.ToString();
}
}
If you're using .NET, use the DirectorySearcher class. You can pass in your domain as a string into the constructor.
// if you domain is domain.com...
string username = "user"
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
"Domain" is not a property of an LDAP object. It is more like the name of the database the object is stored in.
So you have to connect to the right database (in LDAP terms: "bind to the domain/directory server") in order to perform a search in that database.
Once you bound successfully, your query in it's current shape is all you need.
BTW: Choosing "ObjectCategory=Person"
over "ObjectClass=user"
was a good decision. In AD, the former is an "indexed property" with excellent performance, the latter is not indexed and a tad slower.
The best way of searching for users is (sAMAccountType=805306368)
.
Or for disabled users:
(&(sAMAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=2))
Or for active users:
(&(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
I find LDAP as not being so light at it was supposed to be.
Also resource for common LDAP queries - trying to find them yourself and you will precious time and definitely make mistakes.
Regarding domains: it not possible in a single query because the domain is part of the user distinguisedName
(DN
) which, on Microsoft AD, is not searchable by partial matching.
You have to perform your search in the domain:
http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx So, basically your should bind to a domain in order to search inside this domain.
If you're using .NET, use the DirectorySearcher class. You can pass in your domain as a string into the constructor.
// if you domain is domain.com...
string username = "user"
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
"Domain" is not a property of an LDAP object. It is more like the name of the database the object is stored in.
So you have to connect to the right database (in LDAP terms: "bind to the domain/directory server") in order to perform a search in that database.
Once you bound successfully, your query in it's current shape is all you need.
BTW: Choosing "ObjectCategory=Person"
over "ObjectClass=user"
was a good decision. In AD, the former is an "indexed property" with excellent performance, the latter is not indexed and a tad slower.
If you're using .NET, use the DirectorySearcher class. You can pass in your domain as a string into the constructor.
// if you domain is domain.com...
string username = "user"
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
You can use following queries
Users whose Logon Name(Pre-Windows 2000) is equal to John
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(sAMAccountName=**John**))
All Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370))
Enabled Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(!userAccountControl:1.2.840.113556.1.4.803:=2))
Disabled Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(userAccountControl:1.2.840.113556.1.4.803:=2))
LockedOut Users
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(lockouttime>=1))
You have to perform your search in the domain:
http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx So, basically your should bind to a domain in order to search inside this domain.
If you're using .NET, use the DirectorySearcher class. You can pass in your domain as a string into the constructor.
// if you domain is domain.com...
string username = "user"
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
The best way of searching for users is (sAMAccountType=805306368)
.
Or for disabled users:
(&(sAMAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=2))
Or for active users:
(&(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
I find LDAP as not being so light at it was supposed to be.
Also resource for common LDAP queries - trying to find them yourself and you will precious time and definitely make mistakes.
Regarding domains: it not possible in a single query because the domain is part of the user distinguisedName
(DN
) which, on Microsoft AD, is not searchable by partial matching.
Source: Stackoverflow.com