I have a .NET MVC 4 application that uses Windows authentication. Some users are administrators, and need to be able to enter data on behalf of other users.
I have a text box where the admin enters the name of another user. How can I check to verify that the text entered is an existing Windows username?
You could use the FindByIdentity method:
string username = "Some username you retrieved from the TextBox";
using (var ctx = new PrincipalContext(ContextType.Domain, "YOUR_DOMAIN"))
using (var user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, username))
{
bool userExists = user != null;
// here you know whether the user exists or not
}
You can query the Active Directory of your organization for this.
DirectoryEntry entry = new DirectoryEntry("LDAP://DomainName");
DirectorySearcher Dsearch = new DirectorySearcher(entry);
String Name="Richmond";
dSearch.Filter = "(&(objectClass=user)(l=" + Name + "))";
See this article:
http://www.codeproject.com/Articles/6778/How-to-get-User-Data-from-the-Active-Directory
Related
I am able to create new users in a remote ActiveDirectory server, which I am doing using an admin account on that server. This is how I do that:
String connection = "LDAP://serveraddress/OU=NewUsers,OU=People,DC=mydomain,DC=local";
usersDirectoryEntry = new DirectoryEntry(connection, "adminUserName","adminPass");
DirectoryEntry newUser = usersDirectoryEntry.Children.Add(userString, "user");
newUser.CommitChanges();
However, immediately after I am trying to set the password using:
UpdatePassword(newUser, "userPasswordMeetsRequirements");
where
private static void UpdatePassword(DirectoryEntry User, String password)
{
User.Properties["pwdLastSet"].Value = -1;
User.Invoke("SetPassword", new object[] { password.Trim() });
User.Properties["userAccountControl"].Value = 0x0200 | 0x10000;
User.CommitChanges();
}
Where User is of type DirectoryEntry (https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry(v=vs.110).aspx)
And on the User.Invoke line I get a System.Reflection.TargetInvocationException --> System.UnauthorizedAccessException: Access is denied.
That admin user should indeed have set password permissions. After looking in the AD logs I found that at the User.Invoke line my program was trying to connect to the AD server using my current credentials on my computer (which is on a different domain).
Why is this happening, and how can I force it to just use the admin account that it used for user creation?
Are you passing that newUser object to UpdatePassword? I would assume it's using the same credentials.
But if not, you can create a new object like this:
var user = new DirectoryEntry(newUser.Path, "adminUserName","adminPass");
UpdatePassword(user, somepassword);
Begining on .NET 3.5 and newer, you can use the System.DirectoryServices.AccountManagement
You can find information about it in System.DirectoryServices.AccountManagement Namespace.
Why don't you try the following :
PrincipalContext context = new PrincipalContext( ContextType.Domain, null, adAdminLogin, adAdminPassword );
UserPrincipal user = UserPrincipal.FindByIdentity( context, adUserLogin );
user.SetPassword( adUserNewPassword );
I'm able swicth to 3 diff domain using this (just changing domain LDAP path)
i don't see why you couldn't.
Domain = "LDAP://domain2.com";
ADUSER = MySuperAdmin;
ADpassword = "123";
DirectoryEntry Entry = new DirectoryEntry(Domain, ADUSER, ADpassword);
DirectoryEntry Entry = new DirectoryEntry(Domain, ADUSER, ADpassword);
DirectorySearcher Search = new DirectorySearcher(Entry);
Search.Filter = string.Format("(&(objectCategory=computer)(cn={0}))", HostName);
SearchResult Result = Search.FindOne();
now check if your are able find a device on your targeted domain
if it work try change the password if you not able it have nothing to do with the C# code but more with "MySuperAdmin" rigth on the domain2 .try direct in mmc you should pop the same error than in your application.
maybee a checkbox "User cannot change password" may cause the fail too
I am using User.Identity.Name to get the user logged in with my web app.
Then I am querying AD to get properties for that user:
string[] sx = User.Identity.Name.Split('\\');
var username = sx[sx.Count() - 1];
DirectorySearcher searcher = new DirectorySearcher();
searcher.Filter = string.Format(Filter, username);
SearchResult user = searcher.FindOne();
Now I want to add domain support:
string[] sx = UserIdentityName.Split('\\');
var username = sx[sx.Count() - 1];
var domain = sx[0];
DirectoryEntry entry = new DirectoryEntry("LDAP://DC=" + domain));
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = string.Format(Filter, username);
SearchResult user = searcher.FindOne();
This does not work, perhaps because the domain is not fully qualified. So I have DC=intra where I would need DC=intra,DC=contoso,DC=com, and DC=sub where I would need DC=sub,DC=intra,DC=contoso,DC=com.
How can I still connect to the correct AD domain? Can I get full qualified username (UPN name) for user that is logged in, or can I get correct AD domain of user logged in, or how can I achieve this?
This won't work as you've discovered. Instead, bind based on the SID using something like this:
WindowsIdentity current = (WindowsIdentity)User.Identity;
DirectoryEntry userObject = new DirectoryEntry("LDAP://<SID=" + current.User.ToString() + ">");
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);
}
}
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.
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