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 + ")";
....
Related
I am trying to get the manager's account for a user account in active directory.
Here's the code I have..
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
DirectoryContext directoryContext = new DirectoryContext(DirectoryContextType.Domain, "MyDomain");
Domain domain = Domain.GetDomain(directoryContext);
// Find MY directory Entry
DirectorySearcher search = new DirectorySearcher(domain.GetDirectoryEntry())
{
Filter = String.Format("(SAMAccountName={0})", "<my user id>")
};
search.PropertiesToLoad.Add("displayName");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("manager");
DirectoryEntry userAccount = search.FindOne()?.GetDirectoryEntry();
As you can see, there's a property called manager that is requested and comes back as
CN=Manager Name,OU=Employee,OU=United Kingdom, OU=CompantUsers, DC=MyDomain, DC=xxx,DC=zzzzz
The CN=Manager Name is the full name, not the LoginID/SAMAccountName (as used when I searched for MY AD entry ... so how can I now find the AD entry for my manager
Ahhh ... When you know the right question to ask then Google knows the answer ... I did not know that the CN..... string was known as a distinguishedName
if (userAccount.Properties["manager"].Value != null)
{
DirectorySearcher search2 = new DirectorySearcher(domain.GetDirectoryEntry())
{
Filter = string.Format("(distinguishedName={0})", userAccount.Properties["manager"].Value)
};
search2.PropertiesToLoad.Add("displayName");
search2.PropertiesToLoad.Add("mail");
search2.PropertiesToLoad.Add("manager");
DirectoryEntry mgrAcc = search2.FindOne()?.GetDirectoryEntry();
}
Technology used: asp.net(C#), MVC, LINQ, Entity
I use the Active Directory for our company website. This system works for all of our employees on site and when we take our laptop offsite and use VPN.
However we recently hired some developers(offsite) who we have given VPN access to. But they are unable to run our code and I am unsure why. We have active directory users set up for them and they can connect to us through VPN.
They are running into two errors.
A username/password error.
The Server is not Operational error.
Does the VPN username/password have to match the active directory account?
If they log into their development machine does that username/password have to match the active directory account?
Is there some restriction on Active Directory and offsite I am unaware of as to why this would work for us but not our off site developers?
Below is the section of code that is giving the error: It errors on the line SearchResult rs = ds.FindOne();
public AD_CurrentUserProfile GetUserProfile(string userName)
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
using (DirectorySearcher ds = new DirectorySearcher(de))
{
ds.SearchScope = SearchScope.Subtree;
ds.Filter = ("(&(objectCategory=person)(objectClass=User)(samaccountname=" + userName + "))");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("manager");
ds.PropertiesToLoad.Add("directreports");
SearchResult rs = ds.FindOne();
AD_CurrentUserProfile userProfile = new AD_CurrentUserProfile();
userProfile.currentUser = GetProfileFromDN(rs.Properties["distinguishedName"][0].ToString());
userProfile.managerProfile = GetProfileFromDN(rs.Properties["manager"][0].ToString(), true);
int departmentID = db.IPACS_Department.First(v => (v.name == userProfile.currentUser.department)).departmentID;
userProfile.ipacs_department = db.IPACS_Department.Find(departmentID);
if (userProfile.currentUser.userName == userProfile.ipacs_department.owner)
{
userProfile.currentUser.isManager = true;
}
// Override for Provana and upper management
if (userProfile.currentUser.department == "Provana" || userProfile.currentUser.department == "JM")
{
userProfile.currentUser.isManager = true;
}
if (rs.Properties["DirectReports"].Count > 0)
{
if (!userProfile.currentUser.isManager)
{
userProfile.currentUser.isSupervisor = true;
}
userProfile.directReports = new HashSet<AD_User>();
foreach (string value in rs.Properties["DirectReports"])
{
userProfile.directReports.Add(GetProfileFromDN(value));
}
}
return userProfile;
}
}
I never pass 'password' anywhere in any of my code, so is this something Active Directory does by default?
A connection to AD will always require windows credentials. Your code, as posted, does not supply any credentials to AD. (You pass in a user name that you are looking up, but that is not the same as supplying credentials for the connection). This will work for users whose machines are attached to the domain...because your network credentials are passed in implicitly. For the external devs, when they VPN in, they supply credentials to the VPN protocol, which allows their machines to access your network, but that doesn't mean their machines are 'joined' to the domain...so AD will still require explicit credentials from them, including a personal password or a service account password that has permissions to access AD.
This line:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
essentially needs to allow a user name and password:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"), userName, pwd)
{
de.AuthenticationType = AuthenticationTypes.None | AuthenticationTypes.ReadonlyServer;
}
...you'll have to experiment with the AuthenticationTypes flags depending on how your network admins have it set up.
I built a test Active Directory server in Window 2008 and I also run the DNS server on it. On my client machine which runs the C# application, I can authenticate the user against the Active directory server using the function below:
public static UserPrincipal GetUserPrincipal(string usrName,string pswd,string domainName)
{
UserPrincipal usr;
PrincipalContext ad;
// Enter Active Directory settings
ad = new PrincipalContext(ContextType.Domain, domainName,usrName,pswd);
//search user
usr = new UserPrincipal(ad);
usr.SamAccountName = usrName;
PrincipalSearcher search = new PrincipalSearcher(usr);
usr = (UserPrincipal)search.FindOne();
search.Dispose();
return usr;
}
In a separate logic I tried to retrieve a user back from the server using a user name. I used the functions below:
public static DirectoryEntry CreateDirectoryEntry()
{
// create AD connection
DirectoryEntry de = new DirectoryEntry("LDAP://CN=Users,DC=rootforest,DC=com","LDAP","password");
de.AuthenticationType = AuthenticationTypes.Secure;
return de;
}
public static ResultPropertyCollection GetUserProperty(string domainName, string usrName)
{
DirectoryEntry de = CreateDirectoryEntry();
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(SamAccountName=" + usrName + ")";
SearchResult results = deSearch.FindOne();
return null;
}
However, I got no response back from the LDAP server at all, not even an exception. Am I missing certain settings on LDAP server, any of you able to see a flaw in my code (pls don't mind the hard code values, I was testing with this code).
As part of my troubleshooting, I confirmed that I can ping to the rootforest.com from the client machine. I confirmed the user with property samaccountname "LDAP" exists. My path seems to be right because when I go onto the LDAP server and type :
dsquery user -name LDAP*
I got the following:
CN=LDAP L. LDAP,CN=Users,DC=rootforest,DC=com
Any help would be greatly appreciated, I've spent most of my day troubleshooting and researching this little bugger and I think it could be something small which I overlooked.
I don't understand why you're using the new PrincipalContext / UserPrincipal stuff in your first example, but fall back to the hard to use DirectoryEntry stuff in your second example.... doesn't really make sense... also: your second function GetUserProperty seems to return null always - typo or not??
Since you're on already using the System.DirectoryServices.AccountManagement (S.DS.AM) namespace - use it for your second task, too! Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
public static ????? GetUserProperty(string domainName, string usrName)
{
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, usrName);
if(user != null)
{
// return what you need to return from your user principal here
}
else
{
return null;
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
I think your code have a few problems:
Why do you return null in your GetUserProperty() function? You should return results instead.
The attribute you are using in your search filter is misspelled. Use sSAMAccountName instead. Furthermore extend your query to search only for user accounts. Here is an example: (&(objectCategory=person)(objectClass=user)(sAMAccountName=usrName))
You could also use the UserPrincipal class to search for an identity in Active Directory. The UserPrincipal class provides a static method called FindByIdentity() to search for a user identity.
Hope, this helps.
I'm trying to use the DomainServices class to retrieve a list of OU's from my Active Directory.
Here's my code:
public List<OrganizationalUnit> FindOrganizationalUnits(string domainName, string domainExtension, string parentOrganizationUnit)
{
string tmpDirectory = String.Format("LDAP://ou={0},dc={1},dc={2}",
parentOrganizationUnit,
domainName,
domainExtension
);
DirectoryEntry directory = new DirectoryEntry(tmpDirectory);
DirectorySearcher searcher = new DirectorySearcher(directory);
searcher.Filter = "(objectClass=organizationalUnit)";
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("displayName");
var organizationalUnits = new List<OrganizationalUnit>();
foreach (SearchResult result in searcher.FindAll())
{
//I just create and return a new OrganizationalUnit object based on the SearchResult result.
organizationalUnits.Add(new OrganizationalUnit(result));
}
return organizationalUnits;
}
Is there some configuration I have to set on my server end to let me use DirectoryServices to query it's AD objects?
Thanks for the help.
What type of app are you running this code from? AD queries have to be made from an authenticated resource. You can either use the current credentials of the user, or pass in a new name/password.
Services usually don't have any issue, running under LocalSystem, but if this is a web app running under IIS standard permissions, it might cause an issue.
Try adding some credentials where you're instantiating your DirectoryEntry class.
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??