Active Directory - Getting "The Server could not be contacted" Error - c#

I'm trying to create a DLL that gets all the users from a certain OU group in Active Directory, but I'm getting this two errors:
System.DirectoryServices.AccountManagement.PrincipalServerDownException: The server could not be contacted. and
System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.
My code is currently this:
public static void GetAllADUsers(string output, string filter, int mode) {
List<string> allUsers = new List<string>();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "LDAP://mydomain.COM", filter);
UserPrincipal qbeUser = new UserPrincipal(ctx);
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
foreach (var found in srch.FindAll()) {
allUsers.Add(found.DisplayName);
}
if(mode == 1)
File.WriteAllText(output, allUsers.ToString());
else
File.AppendAllText(output, allUsers.ToString());
}
My input parameters are:
PCControl.PCC:GetAllADUsers("C:\temp\users.csv", "OU=Users,OU=...,OU=...,DC=mydomain,DC=com", 1).
I don't know if this helps, but I'm able to use this function into my DLL:
(That's where I got the PrincipalContext)
new DirectorySearcher(new DirectoryEntry("LDAP://mydomain.COM", user, password) {
AuthenticationType = AuthenticationTypes.Secure,
Username = user,
Password = password
}) {
Filter = "(objectclass=user)"
}.FindOne().Properties["displayname"][0].ToString();
Any sugestions on how I can fix this?

Related

PrincipalSearcher Wrong Username Or Password error

I'm trying to get user data from the active directory. Authentication process is true.
var context = new PrincipalContext(ContextType.Domain, "localhost-ad.local", "OU=LocalOu,DC=localhost-ad,DC=local", ContextOptions.Negotiate);
var login = context.ValidateCredentials("user.name", "password", ContextOptions.Negotiate);
But PrincipalSearcher returns a wrong username or password.
var user = new UserPrincipal(context);
var searcher = new PrincipalSearcher(user);
var searchResults = searcher.FindAll();
List<UserPrincipal> results = new List<UserPrincipal>();
foreach (Principal p in searchResults)
{
results.Add(p as UserPrincipal);
}
return results;
How can I solve this problem?
I think your problem may stem from a misunderstanding of what PrincipalContext.ValidateCredentials does.
It does not authenticate the context on the domain or allow access to the domain in any way, all it does is check if the username and password are correct and return True or False.
Instead you can authenticate with the domain as so:
var context = new PrincipalContext(ContextType.Domain, "localhost-ad.local",
"OU=LocalOu,DC=localhost-ad,DC=local", "user.name", "password");

How can I get all Active Directory Users username and display name

I have an Intranet application using ASP.Net MVC 5
I need to query all Active directory users username
I searched the net I found 2 useful post:
1- How can I get a list of users from active directory?
2- http://www.codeproject.com/Tips/599697/Get-list-of-Active-Directory-users-in-Csharp
When I query I can get the Name property but I can't get the active directory usernames
any solutions that I can get all Active directory users username?
bellow is my code:
List<TempUsers> MyTempUser = new List<TempUsers>();
var context = new PrincipalContext(ContextType.Domain, "MyDomain.com");
var searcher = new PrincipalSearcher(new UserPrincipal(context));
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
MyTempUser.Add(new TempUsers { UserName = de.Properties["Name"].Value.ToString() });
}
The property name you're looking for is sAMAccountName.
Here's a list of the available attributes.
I did it this way it worked great:
1- Add reference to Active Directory services DLL
2- Add using in your controller:
using System.DirectoryServices.AccountManagement;
than I created a function to store All Active directory users in database table
bellow is the code hope help someone needs it.
public ActionResult Create()
{
List<MyADUsers> TheAllADUsers = new List<MyADUsers>();
var context = new PrincipalContext(ContextType.Domain, "MyDoman.org");
var searcher = new PrincipalSearcher(new UserPrincipal(context));
foreach (var result in searcher.FindAll())
{
TheAllADUsers.Add(new MyADUsers { ADUserName = result.SamAccountName, AD_IsMemberOf = result.UserPrincipalName, FullName = result.Name });
}
db.MyADUsersContext.AddRange(TheAllADUsers);
db.SaveChanges();
return View();
}

Trouble Authenticating ASP.NET Users Against Active Directory

I replaced our domain name with "demo"... please ignore missing commas and such in the image below.
My question is as follows:
I want to authenticate the SBSUsers in my ASP.NET web application. I cannot figure out what my active directory path needs to be in order to get it to work...
When I set it as follows, it fails to authenticate (I assume because my users are not under that path)... but it doesn't give me an error:
string adPath = "LDAP://ac-dc01.demo.local:389/CN=Configuration,DC=demo,DC=local";
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(adPath, domainAndUsername, pwd);
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
// Update the new path to the user in the directory
adPath = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
When I set it to what I think it should be, it errors on the entry.NativeObject line.
string adPath = "ldap://ac-dc01.demo.local:389/OU=SBSUsers,OU=Users,OU=MyBusiness,DC=demo,DC=local";
Any ideas? Do I need to open it up for "global" access somehow? If so, how would I go about doing that?
I was able to successfully connect using another piece of software...
this is what you can try.. also are you sure that your DC=Demo and DC=Local those look like OU's to me
const string Domain = "ServerAddress:389";
const string constrParts = #"OU=Users,DC=domain,DC=com";
const string Username = #"someusername";
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, Domain, constrParts);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, username);
This is how we connect to our AD and it works great:
<yourConfig>LDAP://ADServerName/OU=GROUPNAME,DC=domainName,DC=com</YourConfig>
And here is a sample code on how you can validate a user:
using (PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain,
ENTER YOUR DOMAIN NAME,
This is where the config that I mentioned above comes in,
ContextOptions.Negotiate,
ENTER YOUR AD SERVICE NAME,
ENTER YOUR AD PASSWORD))
{
UserPrincipal oUser = UserPrincipal.FindByIdentity(oPrincipalContext, THE USERNAME THAT YOU WANT TO VALIDATE);
if (oUser != null)
{
oADAcct = new CUserADAcct();
oADAcct.dumpAcctAttrs(oUser);
}
}

c# Log In click event using authentication against Active Directory through LDAP

I am quite new to c# and LDAP, I'm doing this project so that I could learn about them in a more hands on approach.
What I'm trying to create is a Log in form that has a log in click event that would authenticate the username and password after the user enters them through the active directory using LDAP.
I have read Managing Directory Security Principals in the .NET Framework 3.5 to be able to understand this subject better and I have also gone through similar topics here this one dealing with the validation in itself (c# - Validate a username and password against Active Directory?) and this one authenticating a username (c# against Active Directory over LDAP)
From the first linked topic I had learned that the following code should do the trick in authenticating a username and password:
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "LDAP://example.string.com/OU=Users, Dc=example, Dc= string, DC=com"))
{
bool isValid = pc.ValidateCredentials(User, Password);
}
So my approach to incorporate this to a click event was as follows:
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "LDAP://example.string.com/OU=Users, Dc=example, Dc= string, DC=com"))
bool isValid = pc.ValidateCredentials(User, Password);
if(isValid)
{
Main m = new Main();
this.Close();
m.Show();
}
else
{
MessageBox.Show("Invalid Username and/or Password","Error!");
textBox1.Clear();
textBox2.Clear();
textBox1.Focus();
}
Which is giving me a bool error of Embedded Statement.
I tried the other approach I had read from the second post which was to use this code which authenticates only Username:
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "LDAP://example.com/OU=Computers,OU=Users,dc=example,dc=com");
UserPrincipal user = UserPrincipal.FindByIdentity(pc, "username");
bool userExists = (user != null);
But I found that I wont be able to authenticate a password using this method as UserPrincipal.FindByPassword does not exist.
I have also tried it this way but again .Password does not exist:
PrincipalContext pc = new PrincipalContext(ContextType.Domain,"LDAP://....");
UserPrincipal qbeUser = new UserPrincipal(pc);
qbeUser.EmployeeId = User;
//.Password does not exist
UserPrincipal qbePassword = new UserPrincipal(pc);
qbePassword.Password = Password;
// create your principal searcher passing in the QBE principal
PrincipalSearcher srchUser = new PrincipalSearcher(qbeUser);
PrincipalSearcher srchPass = new PrincipalSearcher(qbePassword);
// try to find that user and password
UserPrincipal founduser = srchUser.FindOne() as UserPrincipal;
UserPrincipal foundpass = srchPass.FindOne() as UserPrincipal;
if (founduser != null)
{
if (foundpass != null)
{
Main m = new Main();
this.Close();
m.Show();
}
else
{
MessageBox.Show("Password Not Valid.");
textBox2.Clear();
textBox2.Focus();
}
}
else
{
MessageBox.Show("Username Not Valid.");
textBox1.Clear();
textBox1.Focus();
}
Can someone kindly please instruct me as how one should correctly approach this.
Thank you in advance.
I have done this but not with PrincipalContext. Instead I have found many people struggling using that object.
My implemenatation was a winforms form and the submit button calls a method executing the 4 las lines of the code below.
I tested against this magnificent free to test LDAP server
var path = "LDAP://ldap.forumsys.com:389/dc=example,dc=com";
var user = $#"uid={username},dc=example,dc=com";
var pass = "password";
var directoryEntry = new DirectoryEntry(path, user, pass, AuthenticationTypes.None);
var searcher = new DirectorySearcher(directoryEntry);
searcher.PropertiesToLoad.Add("*");
var searchResult = searcher.FindOne();
I donĀ“t understand exactly what all of this lines does.
Important tips
On the path the "LDAP://" string should be on block mayus.
In the user, according to the test server you use "cn=username-admin" for validating admins, be sure to also set Authentication type to ServerBind.

Ldap connection in .net C#

I have an application where I can send emails. Now am asked to use ldap to authenticate the user email. Am very new to this concept. I have been given a ldap server link. No idea how to proceed with that. Any article or hits will be greatly helpful.
Here is the code am trying with
public static UserDetail GetUserDetails(string EmailId, string domainName)
{
UserDetail userDetail = new UserDetail();
try
{
string filter = string.Format("(&(ObjectClass={0})(sAMAccountName={1}))", "person", EmailId);
string[] properties = new string[] { "fullname" };
DirectoryEntry adRoot = new DirectoryEntry("LDAP://" + domainName, null, null, AuthenticationTypes.Secure);
DirectorySearcher searcher = new DirectorySearcher(adRoot);
searcher.SearchScope = SearchScope.Subtree;
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.PropertiesToLoad.AddRange(properties);
searcher.Filter = filter;
SearchResult result = searcher.FindOne();
DirectoryEntry directoryEntry = result.GetDirectoryEntry();
string displayName = directoryEntry.Properties["displayName"[0].ToStrin();
string firstName = directoryEntry.Properties["givenName"][0].ToString();
string lastName = directoryEntry.Properties["sn"][0].ToString();
string emailId = directoryEntry.Properties["mail"][0].ToString();
userDetail.EmailId = emailId;
}
catch (Exception)
{
}
return userDetail;
}
I want to achieve it on click of search button. How do I call the method and pass variables.
If you're on .NET 3.5 or newer, you can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a UserPrincipal
// and with the e-mail of "bruce#example.com"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.EmailAddress = "bruce#example.com";
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// try to find that user
UserPrincipal found = srch.FindOne() as UserPrincipal;
if(found != null)
{
// do whatever here - "found" is the user that matched the e-mail given
}
else
{
// there wasn't any user with that e-mail address in your AD
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.
Given the input of emailAddress (type string) this code will search the LDAP directory for a user with a matching email address and return some information on the user:
string fullName = string.Empty;
string givenName = string.Empty;
string distinguishedName = string.Empty;
string sAMAccountName = string.Empty;
using (var context = new PrincipalContext(ContextType.Domain, "DOMAIN"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (Principal result in searcher.FindAll())
{
var de = result.GetUnderlyingObject() as DirectoryEntry;
if (de.Properties["cn"].Value.ToString().Contains(" "))
{
//var userEntry = new DirectoryUser(de.Properties["sAMAccountName"].Value.ToString());
var currentUserEmail = de.Properties["mail"].Value.ToString().ToLower();
if (currentUserEmail == emailAddress)
{
if (de.Properties["cn"].Value != null)
fullName = de.Properties["cn"].Value.ToString();
if (de.Properties["givenName"].Value != null)
givenName = de.Properties["givenName"].Value.ToString();
if (de.Properties["distinguishedName"].Value != null)
distinguishedName =de.Properties["distinguishedName"].Value.ToString();
if (de.Properties["sAMAccountName"].Value != null)
sAMAccountName = de.Properties["sAMAccountName"].Value.ToString();
}
}
}
}
}
It requires a reference to :
System.DirectoryServices;
System.DirectoryServices.AccountManagement;
One caveat I would like to mention is, directory look up routines can be quite slow. If you have 100,000 users on your domain, this process will take a while to run. WHat I tend to do, is dump the output of a directory search to a database table on a regular basis, and perform any lookups on that table. The frequency of the database dumps will of course depend on your business logic. Sometimes I simply truncate the table before performing a new dump, and in other circumstances, I dump to a 'staging' table, and only apply 'delta' updates to the active directoy record table.
Connect to the directory server, using SSL if possible. Promoting a non-secure connection to a secure connection with the StartTLS extended operation is also possible.
Transmit a SEARCH request to the server that contains the base DN from which the client wishes the search to begin, the scope of the search (base, one-level, or sub-tree), a filter using the information the LDAP client knows that will narrow the search results to the desired user, and the attribute 1.1.
The server will respond with the a SEARCH response containing the number of entries that matched the search request parameters and the distinguished names of each entry that matched.
Transmit a BIND request to the directory server over the secure connection. The BIND request contains a distinguished name and the credentials for the distinguished name
The directory server will verify the credentials and return a BIND response with an integer result code indicating whether the credentials matched those stored in the server database
see also
LDAP: programming practices
LDAP: search practices

Categories

Resources