i'm a newbie in ASP.NET so i have a code that i need to optimize it and make it faster because it takes a very long time to be excuted .
this part where it takes time :
public bool IsAuthenticated(String domain, String username, String pwd)
{
img = null;
String domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{//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.
_path = result.Path;
if (result.Properties["cn"].Count > 0)
_userName = _filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
Please Helps !!!
To authenticate, I recommend use PrincipalContext instead.
(This code is in production)
namespace Contoso.Security
{
internal class LDAPService : ILDAPService
{
private readonly ILogger<LDAPService> _logger;
public LDAPService(ILogger<LDAPService> logger)
{
_logger = logger;
}
public bool ValidateCredentials(string domain, string user, string password)
{
try
{
using PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain, "ou=ADAM Users,O=Microsoft,C=US");
return domainContext.ValidateCredentials(user, password, ContextOptions.Negotiate);
}
catch (Exception ex)
{
_logger.LogCritical(ex,ex.Message);
return false;
}
}
}
}
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 :)
I'm trying to remove a certain user from an Active Directory group using C#.
Here is my piece of code which should handle my task, even though it does not currently work.
public static bool RemoveUserFromGroup(string UserId, string GroupId)
{
using (var directory = new DirectoryEntry("LDAP://server"))
{
using (var dSearch = new DirectorySearcher(directory))
{
try
{
dSearch.Filter = "(sAMAccountName=" + UserId + ")";
SearchResult sr = dSearch.FindOne();
System.DirectoryServices.PropertyCollection UserProperties = sr.GetDirectoryEntry().Properties;
if(UserProperties == null)
return false;
foreach(object Group in UserProperties["memberOf"])
{
if(Group.ToString() == GroupId)
{
UserProperties["memberOf"].Remove(GroupId);
directory.CommitChanges();
directory.Close();
return true;
}
}
}
catch (Exception e)
{
return false;
}
}
}
return false;
}
Please excuse me if there are any typos within this code, I had to manually copy it from the machine I am developing on, which has no internet access sadly.
Use:
public void RemoveUserFromGroup(string userId, string groupName)
{
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "COMPANY"))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
group.Members.Remove(pc, IdentityType.UserPrincipalName, userId);
group.Save();
}
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//doSomething with E.Message.ToString();
}
}
public string RemoveUserFromList(string UserID, string ListName)
{
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "DomainName", UserName, Password))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, ListName);
group.Members.Remove(pc, IdentityType.SamAccountName, UserID);
group.Save();
}
return "Success";
}
catch (Exception ex)
{
return ex.Message;
}
}
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 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