I have a code that I use to check if the user is member of the AD, worked perfectly,
now I want to add the possibility to check if the user also a member of a group!
what do I need to modify to achieve that, I did some work, but it fails!
so here is my code:
//Authenticate a User Against the Directory
private bool Authenticate(string userName,string password, string domain)
{
if (userName == "" || password == "")
{
return false;
}
bool authentic = false;
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,userName, password);
object nativeObject = entry.NativeObject;
authentic = true;
}
catch (DirectoryServicesCOMException) { }
return authentic;
}
I want to make it like this:
private bool Authenticate(string userName,string password, string domain, string group)
This is not available on Windows XP or earlier.
Anyway, in order to check for group membership, you can use this code:
bool IsInGroup(string user, string group)
{
using (var identity = new WindowsIdentity(user))
{
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(group);
}
}
In ASP.Net you will use Page.User.IsInRole("RoleName") or in Windows you can use System.Threading.Thread.CurrentPrincipal.IsInRole("RoleName")
I solve it with this code
public bool AuthenticateGroup(string userName, string password, string domain, string group)
{
if (userName == "" || password == "")
{
return false;
}
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain, userName, password);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=user)(|(cn=" + userName + ")(sAMAccountName=" + userName + ")))";
SearchResult result = mySearcher.FindOne();
foreach (string GroupPath in result.Properties["memberOf"])
{
if (GroupPath.Contains(group))
{
return true;
}
}
}
catch (DirectoryServicesCOMException)
{
}
return false;
}
it works fine for me, and it can be use with a machine not part of the Domain Controller / Active Directory
Thank you all for the help
Related
private static bool ValidateUser(string userName, string password, string ldapPath)
{
DirectoryEntry directoryEntry = new DirectoryEntry(ldapPath, userName, password, AuthenticationTypes.ReadonlyServer);
try
{
object obj = directoryEntry.NativeObject;
if (obj.IsNotNullRef())
{
return true;
}
}
catch (Exception ex)
{
//error handling
}
finally
{
directoryEntry.Dispose();
}
return false;
}
I have the sample snippet above that validates an active directory username & password successfully if the domain NETBIOS and DNS match.
However, if the Domain name (NETBIOS) is not matching the DNS entry of the domain, i.e when the NETBIOS & DNS have been registered differently, the code doesn't return true even when you provide a valid UserName and Password.
How can i work around this issue?
EDIT:
The sample input is just standard, a userName, password & a domain URL
Example that returns True:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://abcd1.xyz1.xx1.org", "abcd1\\stacktrace1", "xxxx", AuthenticationTypes.ReadonlyServer);
Example that returns false:
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://abcd2.xyz2.xx2.org", "abcd2\\stacktrace2", "xxxx", AuthenticationTypes.ReadonlyServer);
The only difference between the 2 examples is that, in example 1, the Domain name (NETBIOS) is matching with the DNS entry while in example 2, NETBIOS & DNS have been registered differently.
Refer following method:
public void AuthenticateUser(string userName, string password)
{
DirectoryUserAuthenticationResponse response = new DirectoryUserAuthenticationResponse();
try
{
// Creating Principal Context.
using (var principalContext = GetPrincipalContext())
{
try
{
// Getting user identity and validating user against active provider.
var aUser = UserPrincipal.FindByIdentity(principalContext, GetIdentitytype(), userName);
if (aUser != null)
{
// To check user account is locked out or not
if (aUser.IsAccountLockedOut())
throw new Exception("UserAccountLockedOut");
// To check user account is disabled or not.
if (aUser.Enabled == false)
throw new Exception("UserAccountDisabled");
// To check user change password on next logon.
if (aUser.LastPasswordSet == null)
throw new Exception("changePassword");
//To check password expiration
if (aUser.LastPasswordSet != null && aUser.PasswordNeverExpires == false)
{
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://" + _directoryServerInfo.IPAddress, userName, password);
try
{
// Bind to the ADsobject to force authentication
object nativeobject = rootDSE.Name;
}
catch (DirectoryServicesCOMException cex)
{
string errorCode = cex.ExtendedErrorMessage.Substring(cex.ExtendedErrorMessage.IndexOf("data", 1));
errorCode = errorCode.Substring(5, 3);
//The commented code below fails to parse properly and throws an exception
// int exErrorCode = int.Parse(errorCode);
int exErrorCode = int.Parse(Regex.Match(errorCode, #"\d+").Value);
if (exErrorCode == (int)PWDFlags.Account_Expired)
throw new Exception("AccountExpired");
if (exErrorCode == (int)PWDFlags.Password_Expiration)
throw new Exception("PasswordExpired");
}
}
// validate the credentials by using principal context method.
var isAuthenticated = principalContext.ValidateCredentials(userName, password);
if (!isAuthenticated)
{
throw new Exception("Invalid your name and passowrd");
}
}
else
throw new Exception("InvalidUsernamePassword");
}
catch (DirectoryServicesCOMException cex)
{
string errorCode = cex.ExtendedErrorMessage.Substring(cex.ExtendedErrorMessage.IndexOf("data", 1));
errorCode = errorCode.Substring(5, 3);
//The commented code below fails to parse properly and throws an exception
// int exErrorCode = int.Parse(errorCode);
int exErrorCode = int.Parse(Regex.Match(errorCode, #"\d+").Value);
if (exErrorCode == (int)PWDFlags.Account_Expired)
throw new Exception("AdminACCExpire");
if (exErrorCode == (int)PWDFlags.Password_Expiration)
throw new Exception("AdminPWDExprire");
else
{
//Else any exception
}
}
}
}
catch (Exception)
{
throw;
}
}
private PrincipalContext GetPrincipalContext()
{
// Creating Principal Context.
PrincipalContext principalContext = null;
string serveraddress = _directoryServerInfo.IPAddress;//+":"+_defdirectoryport ;
if (string.IsNullOrEmpty(_directoryfilterouname))
{
principalContext = new PrincipalContext(ContextType.Domain, serveraddress, _directoryAdminUserId, _directoryAdminPassword);
//principalContext = new PrincipalContext(ContextType.Domain, _directoryServerInfo.IPAddress , _directoryAdminUserId, _directoryAdminPassword);
}
else
{
//string domainComponents = GetDomainComponents();
principalContext = new PrincipalContext(ContextType.Domain, serveraddress, _directoryfilterouname, _directoryAdminUserId, _directoryAdminPassword);
}
//// _directoryServerInfo.HostName = //((System.DirectoryServices.AccountManagement.ADStoreCtx)(principalContext.QueryCtx)).DnsDomainName;
return principalContext;
}
I'm writing some kind of a mini AD tool (with VS-C#) to our organization and got into an issue.
I have a main function that searches the user (when I click on it in a listview) and some functions that manipulate the user's object.
public DirectoryEntry GetUser(string username)
{
try
{
Forest currentForest = Forest.GetCurrentForest();
GlobalCatalog gc = currentForest.FindGlobalCatalog();
using (DirectorySearcher searcher = gc.GetDirectorySearcher())
{
searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + username + "*))";
SearchResult results = searcher.FindOne();
if (!(results == null))
{
DirectoryEntry de = new DirectoryEntry(results.Path, strAdminUser, strAdminPass, AuthenticationTypes.Secure);
de.RefreshCache(new string[] { "canonicalName" });
de.Path = de.Properties["canonicalName"].Value.ToString();
de.CommitChanges();
return de;
}
else
{
return null;
}
}
}
catch (DirectoryServicesCOMException e)
{
System.Windows.Forms.MessageBox.Show(e.Message);
return null;
}
}
and here's an example of a function that checks if the user is locked:
public bool IsUserLocked(string username)
{
try
{
DirectoryEntry de = GetUser(username);
string attName = "msDS-User-Account-Control-Computed";
de.RefreshCache(new string[] { attName });
const int UF_LOCKOUT = 0x0010;
int userFlags = /*(int)*/Convert.ToInt32(de.Properties[attName].Value);
if ((userFlags & UF_LOCKOUT) == UF_LOCKOUT)
{
return true;
}
de.Dispose();
return false;
}
catch (DirectoryServicesCOMException e)
{
System.Windows.Forms.MessageBox.Show(e.Message);
return false;
}
}
The function that checks the locked status of a user always fails with an error: "Unspecified error", but if I'm not changing the Directory Entry's path in the first function I get "The server is unwilling to process the request" error (I'm using proper service username and password with all the permissions needed) but still it happens.
Can someone spot the issue?
How about using System.DirectoryServices.AccountManagement namespace? If you have no issue using the new namespace, there's a simpler way to check if user account is locked and unlock if needed.
public bool IsUserLocked (string username)
{
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "yourdomain.com")
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username)
{
if (user != null) return user.IsAccountLockedOut();
}
}
return null;
}
And similarly, you can unlock the user account if needed.
...
if (user != null)
{
user.UnlockAccount();
user.Save();
}
Got it...
This has solved my issue:
de.Path = results.Path.Replace("GC://DCNAME.", "LDAP://");
Since I'm searcing on the Global Catalog, I had to replace a portion in the path to match it to the correct path:
public DirectoryEntry GetUser(string username)
{
try
{
Forest currentForest = Forest.GetCurrentForest();
GlobalCatalog gc = currentForest.FindGlobalCatalog();
using (DirectorySearcher searcher = gc.GetDirectorySearcher())
{
searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + username + "*))";
SearchResult results = searcher.FindOne();
if (!(results == null))
{
DirectoryEntry de = new DirectoryEntry(results.Path, strAdminUser, strAdminPass, AuthenticationTypes.Secure);
de = new DirectoryEntry(results.Path);
de.Path = results.Path.Replace("GC://DCNAME.", "LDAP://");
de.CommitChanges();
//System.Windows.Forms.MessageBox.Show(de.Path);
return de;
}
else
{
return null;
}
}
}
catch (DirectoryServicesCOMException e)
{
System.Windows.Forms.MessageBox.Show(e.Message);
return null;
}
}
Now the path returns to the function called GetUser in the correct format :)
A lot of people posted about this but could not get anything to work. I am trying to get the user's username and password on an Asp.net form (the same username and password which the user uses to login to their computer on a domain).
I am using the PrincipalContext to validate the user.
Although I provide valid username and password, but pc.ValidateCredentials always returns false.
This is the first time I am doing User Authentication through Active Directory and have no idea what else do I require to successfully validate a user from Active Directory.
Do I need to provide information in the Container and Name properties on the PrincipalContext Object as it appears to be null.
Currently I am running this code from local machine which is on domain.
Do you have the correct domain? Maybe it is called different than 'DOMAIN', try this one:
private bool Authenticate(string user, string password)
{
using ( var context = new PrincipalContext(ContextType.Domain, Environment.UserDomainName) ) {
return context.ValidateCredentials(user.Trim(), password.Trim());
}
}
Please use below function
private bool AuthenticateAD(string userName, string password, string domain, out string message)
{
message = "";
DirectoryEntry entry = new
DirectoryEntry("LDAP://" + domain, userName, password);
try
{
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;
}
}
catch (Exception ex)
{
message = ex.Message;
return false;
//throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
I'd like check the the login and the password match with the AD info. I tried with this piece of coode but I get an exception on FindOne (bad username or password .. but they are correct). I know there is the PrincipalContext solution but I need to be able to set the server (Production, Dev, ...)
Thanks,
var Ad = new DirectoryEntry("LDAP://server1.domain.com", username, password);
var AdSearcher = new DirectorySearcher(Ad);
AdSearcher.Filter = String.Format("(anr={0})", username);
AdSearcher.PropertiesToLoad.Add("sAMAccountName");
AdSearcher.PropertiesToLoad.Add("displayName");
var AdSearcherResults = AdSearcher.FindOne();
var userFullName = AdSearcherResults.Properties["displayName"][0].ToString();
var userUid = AdSearcherResults.Properties["sAMAccountName"][0].ToString();
if (Membership.ValidateUser(username, userUid))
return true;
return false;
Update1 I tried this too :
using (var context = new PrincipalContext(ContextType.Domain, "server1.domain.com"))
{
var isValid = context.ValidateCredentials(username, password);
}
My computer is not connected on the domain but should be work I think.
My code for ActiveDirectory Auth.
public DirectoryEntry connDirectory(string usr, string pwd)
{
string ip = iniMan.IniRead("LDAP", "adres");
DirectoryEntry oDE;
oDE = new DirectoryEntry(ip, usr, pwd, AuthenticationTypes.Secure);
return oDE;
}
public bool AD_Login(string kullanici_adi, string sifre)
{
try
{
DirectoryEntry entLogin = connDirectory(kullanici_adi, sifre);
object loginObj = entLogin.NativeObject;
return true;
}
catch (Exception ex)
{
return false;
}
}
void TestMetod(){
if(AD_Login("ozan","ozan"){
//ok
}
}
I just want to Add a new method on an existing code below.
Method is a simply check a given User_ID if it is exists on the AD.
It's my 1st time dealing with AD.
public class AD
{
// Fields
private static string ADPassword = ConfigurationManager.AppSettings["ADPassword"].ToString();
private static string ADPath = ConfigurationManager.AppSettings["ADConnection"].ToString();
private static string ADServerName = ConfigurationManager.AppSettings["ADServerName"].ToString();
private static string ADUserName = ConfigurationManager.AppSettings["ADUserName"].ToString();
// Methods
public static string GetLogin(string sUserName, string sPassword)
{
try
{
DirectoryEntry entry = new DirectoryEntry(ADPath, ADServerName + sUserName, sPassword);
object nativeObject = entry.NativeObject;
return string.Empty;
}
catch
{
return "Invalid Username or Password";
}
}
public static string Update(string sUserName, string sOldPassword, string sNewPassword)
{
string message;
try
{
DirectoryEntry searchRoot = new DirectoryEntry();
searchRoot.Path = ADPath;
searchRoot.Username = ADServerName + ADUserName;
searchRoot.Password = ADPassword;
DirectorySearcher searcher = new DirectorySearcher(searchRoot);
searcher.Filter = "(SAMAccountName=" + sUserName + ")";
DirectoryEntry directoryEntry = searcher.FindOne().GetDirectoryEntry();
directoryEntry.Invoke("ChangePassword", new object[] { sOldPassword, sNewPassword });
directoryEntry.CommitChanges();
directoryEntry.Close();
message = string.Empty;
}
catch (Exception exception)
{
try
{
message = exception.InnerException.Message;
}
catch
{
message = exception.Message;
}
}
return message;
}
}
Which version of the .NET Framework are you on??
In .NET before 3.5, you could probably do a DirectorySearch on the whole server (or alternatively a more constrained subtree):
public bool UserExists(string userName)
{
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://dc=yourcompany,dc=com", userName, password);
DirectorySearcher searchForUser = new DirectorySearcher(searchRoot);
searchForUser.SearchScope = SearchScope.SubTree;
searchForUser.Filter = string.Format("(&(objectCategory=Person)(anr={0}))", userName);
if(searchForUser.FindOne() != null)
{
return true;
}
else
{
return false;
}
}
This is just off the top of my head, can't test it right now. This will search in your entire domain - check the LDAP path for the searchRoot - it would have to be something like
LDAP://dc=yourcompany,dc=com
or if you want to search just inside the "Users" container:
LDAP://cn=Users,dc=yourcompany,dc=com
With .NET 3.5 things got a lot easier - see this MSDN Article for a lot of useful info on how to search and find users and groups in .NET 3.5 using the new System.DirectoryServices.AccountManagement namespace. You can basically now do a FindByIdentity call:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
UserPrincipal foundUser = UserPrincipal.FindByIdentity(ctx, "your user name");
and that's all there is.
Marc
If it's your first AD experience, it might be worth taking a look at this codeproject article: Howto: (Almost) Everything In Active Directory via C#. It contains lots of examples that might help you.
What do you mean exactly by User_ID? Account name? LDAP distinguished name?