wpf - LDAP always returns false when validating - c#

My login window uses LDAP to authenticate users. However, when validating, it always returns false.
Here is the code for validation which I got from CodeProject:
public bool fnValidateUser()
{
bool validation;
try
{
LdapConnection lcon = new LdapConnection
(new LdapDirectoryIdentifier((string)null, false, false));
NetworkCredential nc = new NetworkCredential(Environment.UserName,
txtPassword.SecurePassword, Environment.UserDomainName);
lcon.Credential = nc;
lcon.AuthType = AuthType.Negotiate;
// user has authenticated at this point,
// as the credentials were used to login to the dc.
lcon.Bind(nc);
validation = true;
}
catch (LdapException)
{
validation = false;
}
return validation;
}
txtPassword.SecurePassword is the PasswordBox. When I enter my password/pin and hit login, it displays the MessageBox for whenever validation is false.
What am I doing wrong?
UPDATE: The exception indicates "The LDAP Server is Unavailable", at this line lcon.Bind(nc);

You can try this sample piece of code.
// the username and password to authenticate
const string domain = "OU=Organization,DC=mydomain,DC=com";
string password = "mypass";
string userName = "myuser";
// define your connection
LdapConnection ldapConnection = new LdapConnection("ldap.mydomain.com:389");
try
{
// authenticate the username and password
using (ldapConnection)
{
// pass in the network creds, and the domain.
var networkCredential = new NetworkCredential(username, password, domain);
// if we're using unsecured port 389, set to false. If using port 636, set this to true.
ldapConnection.SessionOptions.SecureSocketLayer = false;
// since this is an internal application, just accept the certificate either way
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
// to force NTLM\Kerberos use AuthType.Negotiate, for non-TLS and unsecured, just use AuthType.Basic
ldapConnection.AuthType = AuthType.Basic;
// authenticate the user
ldapConnection.Bind(networkCredential);
}
catch (LdapException ldapException)
{
//Authentication failed, exception will dictate why
}
}

I went ahead and found another approach for this, without using LDAP.
PrincipalContext adContext = new PrincipalContext(ContextType.Machine);
private async void btnLogin_Click(object sender, RoutedEventArgs e)
{
try
{
using (adContext)
{
if (adContext.ValidateCredentials(txtUsername.Text, txtPassword.Password))
{
MainWindow main = new MainWindow();
main.Show();
main.txtLoggedInUser.Text = UserPrincipal.Current.DisplayName;
this.Close();
}
else
{
MessageBox.Show("Incorrect Username or Password!");
}
}
}
catch(Exception ex)
{
var exceptionDialog = new MessageDialog
{
Message = { Text = ex.ToString() }
};
await DialogHost.Show(exceptionDialog, "RootDialog");
}
}

Related

quickest way to check if a user exists in an active directory group in c#

I am writing a method to determine if a user exists in an Active Directory Group. I may not know this user's password but I do have another username/password in this Active Directory Group. Is there a more efficient way to do this? Setting the SamAccountName property and the call to userFound.GetGroups() seems to be bottlenecks.
Any suggestions are appreciated.
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, ipaddress, remoteDomainAndUserName, password))
{
UserPrincipal qbeUser = new UserPrincipal(pc);
try
{
qbeUser.SamAccountName = lookUpUserName; // don't know password of this user
aDResult = ADResult.Valid; // right now remoteDomainAndUserName/password is valid on the domain, don't know if lookUpUserName is a valid user yet
}
catch (Exception e)
{
return ADResult.InvalidNonLookupID;
}
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
foreach (var found in srch.FindAll())
{
UserPrincipal userFound = found as UserPrincipal;
if (userFound != null)
{
foreach (Principal p in userFound.GetGroups())
{
if (p.SamAccountName.ToLower().Trim() == groupName)
{
bool isEnabled = true;
if (userFound.Enabled.HasValue)
{
isEnabled = userFound.Enabled.Value;
}
if (isEnabled)
return ADResult.ValidInGroup;
else
return ADResult.DisabledInGroup;
}
else
aDResult = ADResult.InvalidInGroup;
}
}
}
}
}
catch (PrincipalServerDownException e)
{
// cannot connect to AD
aDResult = ADResult.Offline;
}
catch (LdapException e)
{
// cannot connect to AD
aDResult = ADResult.Offline;
}
catch (Exception e)
{
// cannot connect to AD
aDResult = ADResult.Offline;
}
//This is a method I use in a WCF web service I created
//userName is the sAMAccount name of the user
//groupName is the AD group
public bool IsMemberOfGroup(string groupName, string userName)
{
try
{
PrincipalContext context = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(context, userName);
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName);
if (group == null)
return false;
if (user != null)
return group.Members.Contains(user);
}
catch (System.Exception ex)
{
//Log exception
}
return false;
}

LDAP server is unavailable exception

I trying to implement LDAP authentication in C# Web Application.
I tried is using the below code.
try
{
using (LdapConnection conn = new LdapConnection(this.Server))
{
string uname = userName;
if (!string.IsNullOrEmpty(this.UsernamePrepend))
{
uname = string.Concat(this.UsernamePrepend, userName);
}
NetworkCredential cred = new NetworkCredential(uname, password, null);
conn.SessionOptions.SecureSocketLayer = true;
conn.SessionOptions.VerifyServerCertificate = (LdapConnection con, X509Certificate cer) => true;
conn.SessionOptions.ProtocolVersion = 3;
conn.AuthType = AuthType.Basic;
conn.Bind(cred);
}
}
catch (LdapException ldapException)
{
LdapException ex = ldapException;
if (!ex.ErrorCode.Equals(49))
{
this.LogError(ex, userName);
throw ex;
}
}
flag = true;
Every time I run it, it goes into catch block with exception LDAP server is unavailable.
Am I missing something?
Remove conn.SessionOptions.SecureSocketLayer = true; from your code

Enable/Disable AD user with LDAP

Is it possible to enable (or disable) a user in Active Directory with LDAP command?
And also, is it possible doing it with C#?
I've already looked
here
and
here
Thanks,
J
using this reference Howto: (Almost) Everything In Active Directory via C#
you can use "userAccountControl" attribute to enable and disable
you need to pass DirectoryEntry to the function
Enable:
public static void Enable(DirectoryEntry user)
{
try
{
int val = (int)user.Properties["userAccountControl"].Value;
user.Properties["userAccountControl"].Value = val & ~0x2;
//ADS_UF_NORMAL_ACCOUNT;
user.CommitChanges();
user.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingWith --> E.Message.ToString();
}
}
Disable :
public void Disable(DirectoryEntry user)
{
try
{
int val = (int)user.Properties["userAccountControl"].Value;
user.Properties["userAccountControl"].Value = val | 0x2;
//ADS_UF_ACCOUNTDISABLE;
user.CommitChanges();
user.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingWith --> E.Message.ToString();
}
}
Using: Morgan Tech Space as Reference:
Enable Active Directory User Account via userAccountControl using C#
private static void EnableADUserUsingUserAccountControl(string username)
{
try
{
DirectoryEntry domainEntry = Domain.GetCurrentDomain().GetDirectoryEntry();
// ldap filter
string searchFilter = string.Format(#"(&(objectCategory=person)(objectClass=user)
(!sAMAccountType=805306370)(|(userPrincipalName={0})(sAMAccountName={0})))", username);
DirectorySearcher searcher = new DirectorySearcher(domainEntry, searchFilter);
SearchResult searchResult = searcher.FindOne();
if (searcher != null)
{
DirectoryEntry userEntry = searchResult.GetDirectoryEntry();
int old_UAC=(int)userEntry.Properties["userAccountControl"][0];
// AD user account disable flag
int ADS_UF_ACCOUNTDISABLE = 2;
// To enable an ad user account, we need to clear the disable bit/flag:
userEntry.Properties["userAccountControl"][0] = (old_UAC & ~ADS_UF_ACCOUNTDISABLE);
userEntry.CommitChanges();
Console.WriteLine("Active Director User Account Enabled successfully
through userAccountControl property");
}
else
{
//AD User Not Found
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Disable Active Directory User Account via userAccountControl using C#
private static void DisableADUserUsingUserAccountControl(string username)
{
try
{
DirectoryEntry domainEntry = Domain.GetCurrentDomain().GetDirectoryEntry();
// ldap filter
string searchFilter = string.Format(#"(&(objectCategory=person)(objectClass=user)
(!sAMAccountType=805306370)(|(userPrincipalName={0})(sAMAccountName={0})))", username);
DirectorySearcher searcher = new DirectorySearcher(domainEntry, searchFilter);
SearchResult searchResult = searcher.FindOne();
if (searcher != null)
{
DirectoryEntry userEntry = searchResult.GetDirectoryEntry();
int old_UAC = (int)userEntry.Properties["userAccountControl"][0];
// AD user account disable flag
int ADS_UF_ACCOUNTDISABLE = 2;
// To disable an ad user account, we need to set the disable bit/flag:
userEntry.Properties["userAccountControl"][0] = (old_UAC | ADS_UF_ACCOUNTDISABLE);
userEntry.CommitChanges();
Console.WriteLine("Active Director User Account Disabled successfully
through userAccountControl property");
}
else
{
//AD User Not Found
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Enable AD User Account via UserPrincipal using C#
private static void EnableADUserUsingUserPrincipal(string username)
{
try
{
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity
(principalContext, username);
userPrincipal.Enabled = true;
userPrincipal.Save();
Console.WriteLine("Active Director User Account Enabled successfully through UserPrincipal");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Disable AD User Account via UserPrincipal using C#
private static void DiableADUserUsingUserPrincipal(string username)
{
try
{
// To use this class, you need add reference System.DirectoryServices.AccountManagement which
is available only from .NET 3.5;
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity
(principalContext, username);
userPrincipal.Enabled = false;
userPrincipal.Save();
Console.WriteLine("Active Director User Account Disabled successfully through UserPrincipal");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
You can use PrincipalContext to enable/ disable AD account.
To Enable the AD you can do something like this:
private static void EnableADUserUsingUserPrincipal(string username)
{
try
{
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity
(principalContext, username);
userPrincipal.Enabled = true;
userPrincipal.Save();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
To disable Active Directory you can just set the userPrincipal.Enabled = false;

Login using WCF service application on a asp.net web application

I'm currently developing a dating site for a school project, and I'mm currently trying to make a log in feature for it. We are not supposed to use the automatic register and login feature.
Any contact we have with the database should go through the WCF service application. I know how to implement it without using the WCF, but I need to use it now, and I can't find this on Google after searching .
public bool login(string UserName, string PassWord, bool isActive = true) {
try {
DALDataContext db = new DALDataContext();
var qry = from m in db.tblUsers
where m.userName == UserName && m.password == PassWord && m.isActive == isActive
select m;
if (qry.Count() > 0) {
return true;
} else {
return false;
}
}
catch (Exception) {
return false;
}
}
That's how I made it, so this should work if I implement it in my web application
like this:
ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client();
protected void btnLoginUser_Click1(object sender, EventArgs e) {
try {
string UserName = txtUserName.Text;
string PassWord = txtPassWord.Text;
obj.login(UserName, PassWord);
if (true) {
Session["me"] = UserName;
Response.Redirect("~/MyProfile.aspx");
}
}
catch (Exception){
}
}
I've been working with this for hours, the register part of this works... so I'm doing something really wrong or something. I'm using Visual Studio 2010 and SQL Server 2008 R2.
[SOLVED]
this is how i solved it
protected void btnLoginUser_Click1(object sender, EventArgs e)
{
try
{
string UserName = txtUserName.Text;
string PassWord = txtPassWord.Text;
bool isActive = true;
if (obj.login(UserName, PassWord, isActive))
{
Session["me"] = UserName;
Response.Redirect("~/MyProfile.aspx");
}
else
{
lblErr.Text = "fail";
}
}
catch (Exception)
{
}
}
}
}
You are ignoring the return value of your login method:
obj.login(UserName, PassWord); // <-- returns true/false.
if (true) // <-- Why?
{
...
Did you mean to do
if (obj.login(UserName, PassWord))
{
Session["me"] = UserName;
Response.Redirect("~/MyProfile.aspx");
} ...
Suggest to return user from WCF service by name, like:
public tblUser login(string UserName);
In the client side you can retrieve user by name:
var user = obj.login(UserName);
if (user != null && user.password == txtPassWord.Text)
DoLogin();
else
ShowError();

Boolean value magically changed to false from ViewModel to View

I need to validate two password fields. If they match then the credential has to be validated in the validation viewmodel. I set a bool value validPassword in the validation viewmodel and need to refer it in the view. Then do something in the view according to validPassword value. However, validPassword always is false when I refer it in the view even though it is true in the viewmodel.
ViewModel:
internal static bool validPassword;
public static bool CheckCredentials(string username, string password, string domain)
{
string userPrincipalName = username + "#" + domain + ".com";
try
{
using(var context = new PrincipalContext(ContextType.Domain, domain))
{
validPassword = true;
return context.ValidateCredentials(userPrincipalName, password);
}
}
catch // a bogus domain causes an LDAP error
{
errorsForPassword.Add("Invalid Login!");
validPassword = false;
return false;
}
}
Code behind the view:
private void PwBox_OnKeyDown(object sender, RoutedEventArgs e)
{
System.Windows.Controls.ToolTip toolTip = new System.Windows.Controls.ToolTip();
//PasswordBox passwordBox = sender as PasswordBox;
passwordAgain = PasswordAgainBox.Password;
if(string.IsNullOrEmpty(passwordAgain) || !string.Equals(passwordAgain, MiscParameterViewModel.password))
{
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Red);
MiscParameterViewModel.nextButtonIsEnabled = false;
if(string.IsNullOrEmpty(passwordAgain))
{
toolTip.Content = "Please enter the password again!";
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
}
else if(!string.Equals(passwordAgain, MiscParameterViewModel.password))
{
toolTip.Content = "Passwords don't match!";
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
}
}
else
{
//ToolTipService.SetToolTip(PasswordAgainBox, null);
//PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
_validationViewModel.Authenticate();
if(!ValidationViewModel.validPassword)
{
toolTip.Content = "Invalid password!";
ToolTipService.SetToolTip(PasswordBox, toolTip);
ToolTipService.SetToolTip(PasswordAgainBox, toolTip);
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Red);
PwBoxAgainBorder.BorderBrush = new SolidColorBrush(Colors.Red);
}
else
{
ToolTipService.SetToolTip(PasswordBox, null);
ToolTipService.SetToolTip(PasswordAgainBox, null);
PwBoxBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
PwBoxAgainBorder.BorderBrush = new SolidColorBrush(Colors.Transparent);
}
}
}
Authenticate is an async method in the viewmodel and it calls CheckCredentials method.
Here is the Authenticate method:
public async void Authenticate()
{
MiscParameterViewModel.nextButtonIsEnabled = false;
NotifyPropertyChanged("NextButtonIsEnabled");
const string propertyKey = "Password";
bool isValid = false;
/* Call service asynchronously */
if(MiscParameterViewModel.servServiceLoginType == ServiceLoginTypes.Windows)
{
if(errorKeys.ContainsKey(propertyKey))
{
errorsForPassword.Clear();
errorKeys.TryRemove(propertyKey, out errorsForPassword);
/* Raise event to tell WPF to execute the GetErrors method */
RaiseErrorsChanged(propertyKey);
}
//if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(passwordAgain))
if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
errorsForPassword.Add("Login is required!");
errorKeys.TryAdd(propertyKey, errorsForPassword);
isValid = false;
}
else
{
isValid = await Task<bool>.Run(() =>
{
return CheckCredentials(username, password, domain);
})
.ConfigureAwait(false);
}
}
What's happening is that your Authenticate method is executing CheckCredentials on another thread, then returning control to your view. What this means is that you will (sometimes) get to this line:
if(!ValidationViewModel.validPassword)
before CheckCredentials has been called. You're seeing false because that's the default value for booleans - it hasn't been set yet.
You could fix this in a couple of different ways. You could return a Task from your authenticate method, and then call .Wait() on the task before checking validPassword.
Or you could simply remove the async/await from your Authenticate method and make it a synchronous method. Which is right depends on the rest of your application.
Edit: Here's my attempt at your authenticate method. I had to guess on some of the functionality you want.
public async Task<bool> Authenticate()
{
MiscParameterViewModel.nextButtonIsEnabled = false;
NotifyPropertyChanged("NextButtonIsEnabled");
const string propertyKey = "Password";
/* Call service asynchronously */
if(MiscParameterViewModel.servServiceLoginType == ServiceLoginTypes.Windows)
{
if(errorKeys.ContainsKey(propertyKey))
{
errorsForPassword.Clear();
errorKeys.TryRemove(propertyKey, out errorsForPassword);
/* Raise event to tell WPF to execute the GetErrors method */
RaiseErrorsChanged(propertyKey);
}
//if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(passwordAgain))
if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
errorsForPassword.Add("Login is required!");
errorKeys.TryAdd(propertyKey, errorsForPassword);
return false;
}
else
{
return await Task<bool>.Factory.StartNew(() => CheckCredentials(username, password, domain));
}
}
return false;
}
Once you get the task back, you'll have to decide what to do with it. If you just call .Wait(), it will work, but you'll get the same problem you had where the GUI freezes while you're waiting.
You might want to use the .ContinueWith() method instead, which will be called once the task is complete, and then in there you can update your password box. You might need to marshall the changes back onto the GUI thread (ContinueWith will be on another thread) to set the password box's values - not sure without the complete solution. Hope that helps

Categories