I read in this question that a common answer to the 0x error also covered in that aforesaid question is often to specify an account under which to search.
I realised that I'm already doing this - in fact, I'm trying to use active directory to authenticate a user to my application. Originally I thought the issue with my error related to how I was formulating my search parameter:
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
Where _path returned the URI. domainAndUsername returned the following:
"domain.com\\usrname"
So I changed the instantiation to:
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
returning "usr#domain.com" (I substituted "#domain.com" because my implementation will require the user to enter their email address and NT password).
That led me to consider the slight possibility that my test might be being performed under an account not privileged enough to perform directory searches - but I've already performed a search for the same data with another implementation - in the same project, no less.
So why does my implementation fall over with 0x80005000 whenever I try to use object obj = entry.NativeObject;?
More importantly, why does this implementation fall over when my original one doesn't? For reference, this implementation is using the same format as demonstrated by Microsoft here, and my original (working) implementation is below:
public class dirSearch
{
public bool searchSuccessful;
public string errStr;
List<string> resList = new List<string>();
public void getEmpDetails(string filStr, string varStr)
{
string strServerDNS = "ldap.domain.com:389";
string strSearchBaseDN = "ou=People,o=domain.com";
string strLDAPPath = "LDAP://" + strServerDNS + "/" + strSearchBaseDN;
DirectoryEntry objDirEntry = new DirectoryEntry(strLDAPPath, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher searcher = new DirectorySearcher(objDirEntry);
SearchResultCollection results;
searcher.Filter = "(uid=" + filStr + ")";
//make sure the order of the search is like so:
//UID
//empnum
//full name
searcher.PropertiesToLoad.Add(varStr);
try
{
results = searcher.FindAll();
foreach (SearchResult result in results)
{
string temStr = result.Properties[varStr][0].ToString();
resList.Add(temStr);
searchSuccessful = true;
}
}
catch (Exception e)
{
errStr = e.ToString();
searchSuccessful = false;
}
}
}
Further information:
I pulled the error code from the exception, which happened to be -2147463168. This corresponds to ADS_BAD_PATHNAME. This is somewhat confusing, as LDAP://ldap.domain.com:389/ is correct. My theory is that I should be using a Distinguished Name, in which case I need to return to my original working method and pull the full name of the staff member, before then formulating the name with that information.
I noticed you're using anonymous authentication to login to your AD, I think you want to use username and password, then see if the user exists. Try the code below:
public bool IsAuthenticated(string username, string pwd)
{
string strLDAPServerAndPort = "ldap.domain.com:389";
string strLDAPContainer = "CN=Users,DC=domain,DC=com";
string strLDAPPath = "LDAP://" + strLDAPServerAndPort + "/" + strLDAPContainer;
DirectoryEntry objDirEntry = new DirectoryEntry(strLDAPPath, username, pwd);
try
{
//Bind to the native AdsObject to force authentication.
object obj = objDirEntry.NativeObject;
DirectorySearcher search = new DirectorySearcher(objDirEntry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
Related
This code works perfectly to get the phone number from Active Directory using the username and password
public string GetPhone(string domain, string username, string pwd)
{
_path = "LDAP://" + domain;
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
string telephoneNumber = string.Empty;
try
{
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
SearchResult result = search.FindOne();
var myEntry = result.GetDirectoryEntry();
telephoneNumber = myEntry.Properties["telephoneNumber"].Value.ToString();
}
catch (Exception ex)
{
throw new Exception("Error obtaining phone number. " + ex.Message);
}
return telephoneNumber;
}
However, I have access to the user password only on the login page. I do have the User context being generated though that is accessible from anywhere within the application (Context.User which is of System.Security.Principal.IPrincipal type)
Thus, how can I get the phone from Active Directory using an already available Context.User object?
Thank you very much in advance
The User object you get will have the SID of the user. With that, you can use the SID binding LDAP path in DirectoryEntry: LDAP://<SID=XXXXX>
var user = new DirectoryEntry(
$"LDAP://<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");
user.RefreshCache(new [] { "telephoneNumber" });
var telephoneNumber = user.Properties["telephoneNumber"]?.Value as string;
The use of RefreshCache is to load only the telephoneNumber attribute. Otherwise, when you first use .Properties, it will retrieve every attribute, which is a waste of time and bandwidth.
Looks like I overcomplicated everything and solution is quite simple
private void SetPhone()
{
DirectoryEntry entryDomain = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher ds = new DirectorySearcher(entryDomain);
string lastName = Context.User.Identity.Name.Split(' ')[Context.User.Identity.Name.Split(' ').Length - 1];
ds.Filter = "(sn=" + lastName + ")";
SearchResult sr = ds.FindOne();
string telephoneNumber = sr.Properties["telephoneNumber"][0].ToString();
telephoneNumber = telephoneNumber.Insert(0, "(").Insert(4, ")").Insert(5, " ").Insert(9, "-");
Session["UserPhone"] = String.Format("{0:(###) ###-####}", telephoneNumber); ;
}
I have this code that works very well when I want to search JUST for the user's last name
public static string GetPhoneFromAD()
{
try
{
DirectoryEntry entryDomain = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher ds = new DirectorySearcher(entryDomain);
string currentContextIdentity = System.Web.HttpContext.Current.User.Identity.Name;
string lastName = currentContextIdentity.Split(' ')[currentContextIdentity.Split(' ').Length - 1];
ds.Filter = "(sn=" + lastName + ")";
SearchResult sr = ds.FindOne();
string telephoneNumber = sr.Properties["telephoneNumber"][0].ToString();
return FormatPhone(telephoneNumber);
}
catch (Exception exception)
{
drmsda.InsertErrorlog("manage.aspx.cs", "Error in an attempt to get the phone number", exception.Source, exception.Message + " " + exception.StackTrace, "");
drmsda.sendErrorEmail("Error: SetPhone generated email", exception.Message);
return string.Empty;
}
}
However, if the user's last name is common, like Smith, I won't the right entry. Thus, I would like to add another criteria, such as first name. However, the query I came up with is not working. I did try the following
ds.Filter = "(givenName=" + firstName + "&sn=" + lastName + ")";
But that is not working, can somebody help?
Thank you in advance
LDAP filter syntax is a bit different to that; you want:
(&(givenName=joe)(sn=bloggs))
See here for many examples.
I'm writing code to move AD user from one OU to another OU. I've read many answers in stackoverflow and I finally came to the following code:
string userToBeMoved = "SomeUserName"; // the username (login name) of a user who exists in AD
string sourceLdapPath = "LDAP://OU=SourceOU,DC=MyDomainName,DC=com";
DirectoryEntry sourceLdapConnection = new DirectoryEntry(sourceLdapPath, adUserName, adPassword);
DirectorySearcher search = new DirectorySearcher(sourceLdapConnection)
{
SearchScope = SearchScope.Subtree,
Filter = "(&" +
"(objectClass=user)" +
"(samaccountname=" + userToBeMoved + ")" +
")"
};
search.PropertiesToLoad.Add("distinguishedname");
SearchResult result = search.FindOne();
using (DirectoryEntry userEntry = result.GetDirectoryEntry())
{
if (userEntry != null)
{
string targetLdapPath = "LDAP://OU=TargetOU,DC=MyDomainName,DC=com";
using (DirectoryEntry targetLdapConnection = new DirectoryEntry(targetLdapPath, adUserName, adPassword))
{
// this is the line at which it breaks
userEntry.MoveTo(targetLdapConnection);
}
}
}
The above code gives the following error:
invalid dn syntax has been specified
The same question is asked in the article (Active Directory move a user to a different OU) however, the answer there is not clear and the code provided is the same code that was provided in the question (which was wrong)
Any help is highly appreciated
I've got a web application that is using Active Directory.
I create a function to perform authentication domain, in parameter ( User,Password, and own domain ).
In determinat line displays a messagem "Unspecified error", as the function below:
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
//Bind to the native AdsObject to force authentication.
---> This Line occurred error
**object obj = entry.NativeObject;**
---> Line Above occurred error
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.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "mypassword")
}
This is probably going to work better for your needs. You need .NET3.5 at least and is what you should be using to authenticate.
I'm attempting to write a program that would automatically create active directory accounts using data from an external data source. The problem that I am running into is that I am always getting an UnAuthorizedAccessException but I for the life of me can't think of what permissions to apply. I've even gone all the way to the root object and given my own account full control which doesn't seem to make any difference. I know that I can access the server since the organizationUnit and de objects are populated correctly.
DirectoryEntry de = new DirectoryEntry("LDAP://MYLOCALADDRESS");
de.Password = "thePassword";
de.Username = "theUserName";
de.AuthenticationType = AuthenticationTypes.Secure ;
DirectoryEntry organizationalUnit = de.Parent;
DirectoryEntry newUser = organizationalUnit.Children.Add("TESTADD ", de.SchemaClassName);
//Exception happens on this line
newUser.CommitChanges();
Any help would be appreciated!
At a glance I'd say your "TESTADD " needs to start with "CN="
For active directory I get all my samples from this codeproject:
public string CreateUserAccount(string ldapPath, string userName,
string userPassword)
{
try
{
string oGUID = string.Empty;
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newUser = dirEntry.Children.Add
("CN=" + userName, "user");
newUser.Properties["samAccountName"].Value = userName;
newUser.CommitChanges();
oGUID = newUser.Guid.ToString();
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
dirEntry.Close();
newUser.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingwith --> E.Message.ToString();
}
return oGUID;
}