I'm trying to find if a user has an Active Directory account by searching with their email address and checking the Enabled property (if I return Enabled as true - run code, if I return false - run other code, and if the results are null - return false because that email doesn't exist anymore). When I get to the foreach loop, it has found the user based on their email in result, but checking with the if and elses returns user as NULL.
public static bool DoesUserExist(string email, string domain)
{
var found = false;
using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain))
{
UserPrincipal user = new UserPrincipal(domainContext);
user.EmailAddress = email;
PrincipalSearcher search = new PrincipalSearcher(user);
search.QueryFilter = user;
PrincipalSearchResult<Principal> results = search.FindAll();
foreach (Principal result in results)
{
if (user.Enabled == true)
{
found = false;
Helpers.LogMessage("Active Directory Account is Enabled in " + domain + " domain");
}
else if (user.Enabled == false)
{
found = true;
Helpers.LogMessage("Active Directory User Account is Disabled in " + domain + " domain");
}
else if (user.Enabled == null)
{
found = true;
Helpers.LogMessage("No Active Directory Account Found in " + domain + " domain");
}
}
return found;
}
}
What am I missing to be able to access if the user is Enabled or Disabled in the foreach?
Your problem is user has nothing to do with the results of your search or the loop; it is just the template for searching. Also note that if no result is found, you will not enter the loop (results will be empty) and so testing for null makes no sense. Also, your found settings seem to be wrong.
if (!results.Any())
Helpers.LogMessage("No Active Directory Account Found in " + domain + " domain");
else {
var found = false;
foreach (UserPrincipal result in results) {
found = !result.Enabled;
if (found)
Helpers.LogMessage("Active Directory User Account is Disabled in " + domain + " domain");
else
Helpers.LogMessage("Active Directory Account is Enabled in " + domain + " domain");
}
}
I am trying to get the name and role of the user who is currently accessing the web application but the code I have written fetches the server user name.
Could you please review the below code that I have written and tell a solution to this problem.
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string[] stringSeparators = new string[] { "\\" };
string[] uname = userName.Split(stringSeparators, StringSplitOptions.None);
userName = uname[1];
List<string> userRoles = new List<string>();
userRoles = getUserRole(userName);
public List<string> getUserRole(string userName)
{
List<string> userNestedMembership = new List<string>();
DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain
DirectorySearcher samSearcher = new DirectorySearcher();
samSearcher.SearchRoot = domainConnection;
samSearcher.Filter = "(samAccountName=" + userName + ")";
samSearcher.PropertiesToLoad.Add("displayName");
SearchResult samResult = samSearcher.FindOne();
if (samResult != null)
{
DirectoryEntry theUser = samResult.GetDirectoryEntry();
theUser.RefreshCache(new string[] { "tokenGroups" });
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0);
DirectorySearcher sidSearcher = new DirectorySearcher();
sidSearcher.SearchRoot = domainConnection;
sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";
sidSearcher.PropertiesToLoad.Add("distinguishedName");
SearchResult sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
string role = (string)sidResult.Properties["distinguishedName"][0];
role = role.Substring(3, role.Length - 3);
string[] roles = role.Split(',');
userNestedMembership.Add(roles[0]);
}
}
}
}
I have not done any changes in web config.
userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
That is getting you the wndows user name, which is likely the apppool name.
Have you set thr web config and IIS to know thet you want to use windows auth?
(or if you are not, try using the HTTP Context
HttpContext.Current.User.Identity.Name
Hope this helps, or at least gives you a steer in the right direction
The issue probably doesn't lie in the code, but in environment configuration. There are specific requirements to be met, to make System.Security.Principal.WindowsIdentity.GetCurrent().Name work as you want, as it gets the user on the server side. Here is nice post describing what could be done to make IIS work under user account (with Windows Authentication).
I am facing a strange issue.
I want to know whether user's AD account is disabled or not by providing user's email as the parameter.
Below code is working great for me for some set of users in our Org.
But for some other set of users its returning null- though I can able to verify these set of users in AD manually.
Can you please help me to over come from this issue.
private string GetCurrentDomainPath()
{
DirectoryEntry de =
new DirectoryEntry("LDAP://RootDSE");
return "LDAP://" +
de.Properties["defaultNamingContext"][0].
ToString();
}
public bool? FindAccountStatusByEmail(string email)
{
using (DirectorySearcher dSearch = new DirectorySearcher(new DirectoryEntry(GetCurrentDomainPath())))
{
dSearch.SearchScope = SearchScope.Subtree;
dSearch.Filter = "(&(objectCategory=person)(sAMAccountName=*)(mail=" + email.Trim() + "))";
SearchResult sResult = dSearch.FindOne();
if (sResult != null)
{
DirectoryEntry de = sResult.GetDirectoryEntry();
return IsActive(de);
}
else
{
return null;
}
}
}
private bool IsActive(DirectoryEntry de)
{
if (de.NativeGuid == null)
{
return false;
}
int flags = (int)de.Properties["userAccountControl"].Value;
return !Convert.ToBoolean(flags & 0x0002);
}
Update-1: Lets say I have one user's email address : abac#mydomain.com
When I passing this email address through the code, its returning null.
But when I am searching it in through windows provided tool(Find Users,Contacts and Groups) I am able to retrieve the user.
But just now noticed that in "E-mail :" section that user's email is different
say abac#mydomainlbs.com in the above picture.
I know that this type of question has been asked before, but other methods are failing me right now.
As it stands our windows service polls AD, given an LDAP (i.e. LDAP://10.32.16.80) and a list of usergroups within that AD server to search for.
It retrieves all users within those given groups, recursively searching those groups for more groups as well.
Each user is then added to another applications authenticated users list.
This part of the application is running successfully. However, we're in need of each user's friendly domain name (i.e. the part of their login DOMAIN/username)
So if there is a user that is part of TEST domain, named Steve: TEST/steve is his login.
I'm able to find steve in the AD, however I also need "TEST" to be stored along with his AD information.
Again, I can find 'steve' fine by using a directory searcher and the LDAP IP I'm given, but given the LDAP IP, how can I find the friendly domain name?
When I try the following code I'm given an error when attempting to access the 'defaultNamingContext':
System.Runtime.InteropServices.COMException (0x8007202A): The authentication mechanism is unknown.
Here is the code:
private string SetCurrentDomain(string server)
{
string result = string.Empty;
try
{
logger.Debug("'SetCurrentDomain'; Instantiating rootDSE LDAP");
DirectoryEntry ldapRoot = new DirectoryEntry(server + "/rootDSE", username, password);
logger.Debug("'SetCurrentDomain'; Successfully instantiated rootDSE LDAP");
logger.Debug("Attempting to retrieve 'defaultNamingContext'...");
string domain = (string)ldapRoot.Properties["defaultNamingContext"][0]; //THIS IS WHERE I HIT THE COMEXCEPTION
logger.Debug("Retrieved 'defaultNamingContext': " + domain);
if (!domain.IsEmpty())
{
logger.Debug("'SetCurrentDomain'; Instantiating partitions/configuration LDAP entry");
DirectoryEntry parts = new DirectoryEntry(server + "/CN=Partitions,CN=Configuration," + domain, username, password);
logger.Debug("'SetCurrentDomain'; Successfully instantiated partitions/configuration LDAP entry");
foreach (DirectoryEntry part in parts.Children)
{
if (part.Properties["nCName"] != null && (string)part.Properties["nCName"][0] != null)
{
logger.Debug("'SetCurrentDomain'; Found property nCName");
if ((string)part.Properties["nCName"][0] == domain)
{
logger.Debug("'SetCurrentDomain'; nCName matched defaultnamingcontext");
result = (string)part.Properties["NetBIOSName"][0];
logger.Debug("'SetCurrentDomain'; Found NetBIOSName (friendly domain name): " + result);
break;
}
}
}
}
logger.Debug("finished setting current domain...");
}
catch (Exception ex)
{
logger.Error("error attempting to set domain:" + ex.ToString());
}
return result;
}
edit
I added this sample method in order to attempt a suggestion but am getting an exception: "Unspecified error" when I hit the "FindAll()" call on the searcher.
The string being passed in is: "CN=TEST USER,CN=Users,DC=tempe,DC=ktregression,DC=com"
private string GetUserDomain(string dn)
{
string domain = string.Empty;
string firstPart = dn.Substring(dn.IndexOf("DC="));
string secondPart = "CN=Partitions,CN=Configuration," + firstPart;
DirectoryEntry root = new DirectoryEntry(secondPart, textBox2.Text, textBox3.Text);
DirectorySearcher searcher = new DirectorySearcher(root);
searcher.SearchScope = SearchScope.Subtree;
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.Filter = "(&(nCName=" + firstPart + ")(nETBIOSName=*))";
try
{
SearchResultCollection rs = searcher.FindAll();
if (rs != null)
{
domain = GetProperty(rs[0], "nETBIOSName");
}
}
catch (Exception ex)
{
}
return domain;
This article helped me much to understand how to work with the Active Directory.
Howto: (Almost) Everything In Active Directory via C#
From this point forward, if you require further assitance, please let me know with proper questions in comment, and I shall answer them for you to the best of my knowledge.
EDIT #1
You had better go with this example's filter instead. I have written some sample code to briefly show how to work with the System.DirectoryServices and System.DirectoryServices.ActiveDirectory namespaces. The System.DirectoryServices.ActiveDirectory namespace is used to retrieve information about the domains within your Forest.
private IEnumerable<DirectoryEntry> GetDomains() {
ICollection<string> domains = new List<string>();
// Querying the current Forest for the domains within.
foreach(Domain d in Forest.GetCurrentForest().Domains)
domains.Add(d.Name);
return domains;
}
private string GetDomainFullName(string friendlyName) {
DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, friendlyName);
Domain domain = Domain.GetDomain(context);
return domain.Name;
}
private IEnumerable<string> GetUserDomain(string userName) {
foreach(string d in GetDomains())
// From the domains obtained from the Forest, we search the domain subtree for the given userName.
using (DirectoryEntry domain = new DirectoryEntry(GetDomainFullName(d))) {
using (DirectorySearcher searcher = new DirectorySearcher()){
searcher.SearchRoot = domain;
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("sAMAccountName");
// The Filter is very important, so is its query string. The 'objectClass' parameter is mandatory.
// Once we specified the 'objectClass', we want to look for the user whose login
// login is userName.
searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", userName);
try {
SearchResultCollection results = searcher.FindAll();
// If the user cannot be found, then let's check next domain.
if (results == null || results.Count = 0)
continue;
// Here, we yield return for we want all of the domain which this userName is authenticated.
yield return domain.Path;
} finally {
searcher.Dispose();
domain.Dispose();
}
}
}
Here, I didn't test this code and might have some minor issue to fix. This sample is provided as-is for the sake of helping you. I hope this will help.
EDIT #2
I found out another way out:
You have first to look whether you can find the user account within your domain;
If found, then get the domain NetBIOS Name; and
concatenate it to a backslash (****) and the found login.
The example below uses a NUnit TestCase which you can test for yourself and see if it does what you are required to.
[TestCase("LDAP://fully.qualified.domain.name", "TestUser1")]
public void GetNetBiosName(string ldapUrl, string login)
string netBiosName = null;
string foundLogin = null;
using (DirectoryEntry root = new DirectoryEntry(ldapUrl))
Using (DirectorySearcher searcher = new DirectorySearcher(root) {
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("sAMAccountName");
searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", login);
SearchResult result = null;
try {
result = searcher.FindOne();
if (result == null)
if (string.Equals(login, result.GetDirectoryEntry().Properties("sAMAccountName").Value))
foundLogin = result.GetDirectoryEntry().Properties("sAMAccountName").Value
} finally {
searcher.Dispose();
root.Dispose();
if (result != null) result = null;
}
}
if (!string.IsNullOrEmpty(foundLogin))
using (DirectoryEntry root = new DirectoryEntry(ldapUrl.Insert(7, "CN=Partitions,CN=Configuration,DC=").Replace(".", ",DC="))
Using DirectorySearcher searcher = new DirectorySearcher(root)
searcher.Filter = "nETBIOSName=*";
searcher.PropertiesToLoad.Add("cn");
SearchResultCollection results = null;
try {
results = searcher.FindAll();
if (results != null && results.Count > 0 && results[0] != null) {
ResultPropertyValueCollection values = results[0].Properties("cn");
netBiosName = rpvc[0].ToString();
} finally {
searcher.Dispose();
root.Dispose();
if (results != null) {
results.Dispose();
results = null;
}
}
}
Assert.AreEqual("FULLY\TESTUSER1", string.Concat(netBiosName, "\", foundLogin).ToUpperInvariant())
}
The source from which I inspired myself is:
Find the NetBios Name of a domain in AD
Since I could not find any example code I would like to share my own solution. This will search the parents of the DirectoryEntry object until it hits the domainDNS class.
using System.DirectoryServices;
public static class Methods
{
public static T ldap_get_value<T>(PropertyValueCollection property)
{
object value = null;
foreach (object tmpValue in property) value = tmpValue;
return (T)value;
}
public static string ldap_get_domainname(DirectoryEntry entry)
{
if (entry == null || entry.Parent == null) return null;
using (DirectoryEntry parent = entry.Parent)
{
if (ldap_get_value<string>(parent.Properties["objectClass"]) == "domainDNS")
return ldap_get_value<string>(parent.Properties["dc"]);
else
return ldap_get_domainname(parent);
}
}
}
Use it like this:
string[] _properties = new string[] { "objectClass", "distinguishedName", "samAccountName", "userPrincipalName", "displayName", "mail", "title", "company", "thumbnailPhoto", "useraccountcontrol" };
string account = "my-user-name";
// OR even better:
// string account = "my-user-name#DOMAIN.local";
using (DirectoryEntry ldap = new DirectoryEntry())
{
using (DirectorySearcher searcher = new DirectorySearcher(ldap))
{
searcher.PropertiesToLoad.AddRange(_properties);
if (account.Contains('#')) searcher.Filter = "(userPrincipalName=" + account + ")";
else searcher.Filter = "(samAccountName=" + account + ")";
var user = searcher.FindOne().GetDirectoryEntry();
Console.WriteLine("Name: " + Methods.ldap_get_value<string>(user.Properties["displayName"]));
Console.WriteLine("Domain: " + Methods.ldap_get_domainname(user));
Console.WriteLine("Login: " + Methods.ldap_get_domainname(user) + "\\" + Methods.ldap_get_value<string>(user.Properties["samAccountName"]));
}
}
I haven't got a forest to test it on but in theory this should cut it.
You can retrieve the name of the domain that the current user is on using the Environment.UserDomainName Property.
string domainName;
domainName = System.Environment.UserDomainName;
Maybe not entirely correct but...
DirectoryEntry dirEntry = new DirectoryEntry();
DirectorySearcher dirSearcher = new DirectorySearcher(dirEntry);
dirSearcher.SearchScope = SearchScope.Subtree;
dirSearcher.Filter = string.Format("(&(objectClass=user)(|(cn={0})(sn={0}*)(givenName={0})(sAMAccountName={0}*)))", userName);
var searchResults = dirSearcher.FindAll();
foreach (SearchResult sr in searchResults)
{
var de = sr.GetDirectoryEntry();
string user = de.Properties["SAMAccountName"][0].ToString();
string domain = de.Path.ToString().Split(new [] { ",DC=" },StringSplitOptions.None)[1];
MessageBox.Show(domain + "/" + user);
}
Because the value of de.Path is
LDAP://CN=FullName,DC=domain,DC=local
I am receiving an unusual behaviour in my asp.net application. I have code that uses Directory Services to find the AD groups for a given, authenticated user. The code goes something like ...
string username = "user";
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
And then I query and get the list of groups for the given user. The problem is that the code was receiving the list of groups as a list of strings. With our latest release of the software, we are starting to receive the list of groups as a byte[].
The system will return string, suddenly return byte[] and then with a reboot it returns string again.
Anyone have any ideas?
(marc_s) Added code sample:
DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + ldapSearchBase);
DirectorySearcher userSearcher = new DirectorySearcher(dirEntry)
{ SearchScope = SearchScope.Subtree,
CacheResults = false,
Filter = ("(" + txtLdapSearchNameFilter.Text + "=" + userName + ")")
};
userResult = userSearcher.FindOne();
ResultPropertyValueCollection valCol = userResult.Properties["memberOf"];
foreach (object val in valCol)
{
if (val is string)
{
distName = val.ToString();
}
else
{
distName = enc.GetString((Byte[])val);
}
}