I have implemented an application that authenticates users against active directory using LDAP. Since users are being authenticated from different domains, they log in by DOMAIN\UserName. After being logged in, I capture the username by using User.Identity.GetUserName() however this, of course, returns DOMAIN\UserName. What I need to do here now is to extract the UserName from the string returned. Any help will be appreciated.
What about User.Identity.GetUserName().Split('\\')[1] ?
I think you're looking for Substring
string FullName = User.Identity.GetUserName();
string UserName = FullName.Substring(FullName.IndexOf("\\"));
(You might have to throw a + 1 right after FullName.IndexOf("\\"))
public string RemoveDomain(string username)
{
if (String.IsNullOrWhiteSpace(username))
return username;
return username.Split('\\').Last();
}
This will handle null and username without domain name as well.
Usage:
var username = RemoveDomain("Domain1\\Username");
username = RemoveDomain("Username");
username = RemoveDomain(null);
Related
I have a need for users to be able to enter an email address and after clicking submit, I'd like to get some Active Directory information from the email address entered. Our usernames unfortunately don't follow a single naming convention so I'm unable to reliably guess the username from the email address entered.
I'm fine with looking up information from a username, is there a way of back tracking to an identity from an email address entered using DirectoryServices or something similar?
Thanks,
Edit and some clarifiction:
On the form, it's not the authenticated users email address that I would be looking up, it's the email address that was entered in the form.
public class ManagerDetails
{
public string GetManagersName(string emailAddress)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal mgr = UserPrincipal.FindByIdentity(ctx, emailAddress);
string managersName = mgr.GivenName;
return managersName;
}
}
This piece of code works, but only if the username matches the email address.
For example, this works, Joe.Bloggs#mydomain.com (email) and Joe.Bloggs (domain username).
If it's Joe.Bloggs#mydomain.com (email) and BloggsJ (domain username) it fails.
This is obviously because it's using the email address as the full username but hopefully it explains what I'm trying to achieve.
I'm still refining but this works, it allows me to use directory searcher to get a name from an email address entered.
public string GetManagersName(string emailAddress)
{
string userName = GetManagersUserNameByEmailAddress(emailAddress);
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal usr = UserPrincipal.FindByIdentity(ctx, userName);
string managersName = usr.GivenName;
return managersName;
}
private string GetManagersUserNameByEmailAddress(string emailAddress)
{
DirectorySearcher adSearcher = new DirectorySearcher();
adSearcher.Filter = ("mail=" + emailAddress);
adSearcher.PropertiesToLoad.Add("samaccountname");
SearchResult result = adSearcher.FindOne();
DirectoryEntry user = result.GetDirectoryEntry();
string userName = user.Properties["samaccountname"].Value.ToString();
return userName;
}
I'm writing a program (WinForms, C#) that runs on a Win 7 client machine. It obtains credentials from the user (user id, password, and subdomain name) and uses them to authenticate (via Active Directory) to other servers to which the program remotely connects. The other servers are on a domain different than the domain the Win 7 client machine is on.
Using the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes, I can test the credentials with no more than the user id, password, and subdomain name (See answer for S.O. Why does Active Directory validate last password?).
For example, for the user account ssmith#xyz.gov, I need only provide ssmith (user id), the password for ssmith, and xyz (the subdomain). I don't need to provide the top-level domain name (gov in this case).
Now, I want to programmatically obtain the top-level domain name for this user account (gov in this case). I've examined the properties and methods of the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes. I've looked over the other classes in the System.DirectoryServices.Protocols Namespace. I don't see a way to programmatically obtain the top-level domain name.
Given user id, password, and subdomain name, how can I obtain the top-level domain name for a user account?
Here is my code. Given a user account of ssmith#xyz.gov, My call looks like this (asterisks represent a SecureString password)
bool result = ValidateCredentials("ssmith","******", "xyz");
Here is my code for the method.
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, SecureString ssPassword, string domain)
{
//suports secure string
NetworkCredential credentials = new NetworkCredential(username, ssPassword, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using (LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
// The only way to test credentials on a LDAP connection seems to be to attempt a
// Bind operation, which will throw an exception if the credentials are bad
connection.Bind();
}
catch (LdapException lEx)
{
credentials = null;
id = null;
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
}
credentials = null;
id = null;
return true;
}
After a successful bind, then the full DNS name of the domain will be in the LdapConnection object:
var domain = connection.SessionOptions.DomainName;
In this case, that would be "xyz.gov". If you need just "gov", then you can just take everything after the last dot:
var tld = domain.Substring(domain.LastIndexOf('.') + 1);
I am writing an ASP.NET program where I need to store the users password in the database. But I get a password mismatched when I Compare the password from the database with the user input password. Even if the users password is correct.
Password Hashing:
string PasswordSalt = Crypto.HashPassword(DateTime.Now.ToString());
string hashPassword = Crypto.HashPassword(formcollection["PassWord"]); //Hash User PassWord
user.PassWord = Crypto.HashPassword(PasswordSalt + hashPassword);//Add Salt to Password For Futher Security
user.PassWordSalt = PasswordSalt;
Password Verification:
Users ThisUser = Users.UsersGetByEmail((string)Session["email"]);
string checkpassword = ThisUser.PassWord;
//User Inputed password.
string password = user.PassWord;
if (password != null)
{
//Need to fix.
string encrypt_password = Crypto.HashPassword(password);
string salted_password = Crypto.HashPassword(ThisUser.PassWordSalt + encrypt_password);
//bool does_password_match = Crypto.VerifyHashedPassword(checkpassword, password);
if (checkpassword == salted_password)
{
//Check if the inputed password matches the password from the Database.
//Remember to give session based on the user_id.
Session["user_id"] = ThisUser.Id;
return RedirectToAction("Promise");
}
else
{
ModelState.AddModelError("PassWord", "Wrong Password, Please Enter Correct Password");
return View(user);
}
I've never used it, but based on the documentation...
Crypto.HashPassword adds the salt for you and returns a base-64 encoded string with all the details in it to verify the password. So, you do NOT need to add a salt yourself.
All you need to do is store the hash result (base64EncodedHash below) in the DB, and then use it with VerifyHashedPassword to authenticate later. E.g. make a unit test like so:
var base64EncodedHash = Crypto.HashPassword("password");
Assert.IsTrue( Crypto.VerifyHashedPassword( base64EncodedHash, "password" ) );
Assert.IsFalse( Crypto.VerifyHashedPassword( base64EncodedHash, "otherPass") );
https://msdn.microsoft.com/en-us/library/system.web.helpers.crypto.verifyhashedpassword(v=vs.111).aspx
To translate this to your code:
user.PassWord = Crypto.HashPassword(formcollection["PassWord"]);
Then to verify (comments added for quirks I see):
//Why are you storing "email" in Session before user is validated??? Seems off.
Users ThisUser = Users.UsersGetByEmail((string)Session["email"]);
string userInputPassword = user.PassWord; //this should be coming from POST
if( ThisUser != null && Crypto.VerifyHashedPassword(ThisUser.PassWord, userInputPassword) ) {
Session["user_id"] = ThisUser.Id;
return RedirectToAction("Promise");
}
else {
ModelState.AddModelError("PassWord","Your username or password are incorrect");
return View(user);
}
Ideally, as I somewhat indicated by my change of your error text...you also want to give the user the same error message whether the username/email or password are wrong. Your code, as is, probably returns a different error if the email doesn't return an account, but you don't want to give that much info to brute-force attackers.
You also need to put in some brute-force checking so that if they attempt too many times with failures, block that IP address for X amount of time., etc.
And, as someone said...when it comes to security...until you're the
expert...it's best to use pre-existing code/frameworks to mitigate you
risks.
I'm working on an intranet, I've just added a feature on the user's profile to change his password.
As you can see with the following controller :
[HttpPost]
public ActionResult ChangePassword(Employee objToEdit, FormCollection form, LocalPasswordModel model) // Find how to obtain "OldPassword" from AccountModel
{
objToEdit.Login = User.Identity.Name;
string name = objToEdit.FirstName;
string pwd = form["NewPassword"];
string confirm = form["ConfirmPassword"];
if (_service.Edit_password(objToEdit, pwd, confirm)) // Checks if NewPassword and ConfirmPassword are the same, and does some syntax checking
{
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ResetPassword(WebSecurity.GeneratePasswordResetToken(objToEdit.Login), pwd); // Seems to work
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("Index", new { Message = CRAWebSiteMVC.Controllers.AccountController.ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
return new RedirectResult(Url.Action("Index"));
}
return View();
}
So far, the user just needs to input a New password and a confirmation password. I wish to add a "Enter your current Password" feature but I can't find a way to retrieve the user's current password !
The user profile DB does not contain a Password column anymore and I use Form authentication if that's of any help.
EDIT: Thank you for your help, to solve my problem I simply replaced the ResetPassword line by the following :
changePasswordSucceeded = WebSecurity.ChangePassword(objToEdit.Login, current, pwd);
If it fails, it directly displays the error message that the current password is wrong.
You can't !
That's actually a security feature. You should never store a password in plain text.
The good thing is, you don't need to do the comparison yourself:
Instead, use something like ValidateUser to let the Membership Provider validate the provided password. Behind the scenes, this method will hash the password and compare it with the hashed version contained in the database.
EDIT:
Also, note that since you are using the WebSecurity class, there is a method, ChangePassword that accepts the current password. It seems that method will check the current password matches the specified currentPassword parameter. Maybe you should use this one instead of ResetPassword
I keep getting the following error when trying to connect to an LDAP server. The user name or password is incorrect.
It occurs on the .FindOne()
If I use AuthenticationTypes.Encryption i get an error: The server is not operational.
I've also tried to prepend the username with ownme\username
I'm extremely newbish with AD so the issue might be so simple.
Domain = domain;
_entry = new DirectoryEntry("LDAP://DC1/DC=ownme,DC=local", username, password, AuthenticationTypes.ServerBind);
_directorySearcher = new DirectorySearcher(_entry, "(objectClass=*)", new string[] {"namingContexts"}, SearchScope.Subtree);
var namingContext = _directorySearcher.FindOne();
The problem was credentials. You need to specify the domain prefix in the username or look at one of the comments in my question.
I had var username = "domain\username";
I should have written var username = #"domain\username";