Timeout issue When Using Active Directory - c#

I'm trying to do a couple of things. First off this c# program verifies against Active Directory user credentials using:
var ADentry = new DirectoryEntry("LDAP://domain", uname, pword);
But obviously you need to pass in the username and password somehow. Is there a way that you can retrieve it automatically when the user signs in on the network from Active Directory and use that in the fields without having the username type in the username and password.
If not, I made it so the user can type in their credentials in the console. But if it doesn't work it ends up hanging forever. What type of code can I use to timeout after say 1 minute if this keeps hanging otherwise it hangs forever? thanks

I was trying to do an LDAP query and the following will do it for you. Most of it are methods but you may be interested in this line of code: PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ContextOptions.Negotiate);
The full code is here:
IsUserGroupMember("username", "group name the user is in");
public string sDomain = "your.domainName.com"
public string sDefaultOU = OU=**,OU=**,DC=**,DC=**,DC=**
public void IsUserGroupMember(string sUserName, string sGroupName)
{
try
{
UserPrincipal oUserPrincipal = GetUser(sUserName);
GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
if (oUserPrincipal != null && oGroupPrincipal != null)
{
//do something
}
else
{
//nothing
}
}
catch (PrincipalServerDownException)
{
throw;
}
catch (Exception)
{
throw;
}
}
public UserPrincipal GetUser(String sUserName)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
return oUserPrincipal;
}
public GroupPrincipal GetGroup(string sGroupName)
{
PrincipalContext oPrincipalContext = GetPrincipalContext();
GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, sGroupName);
return oGroupPrincipal;
}
public PrincipalContext GetPrincipalContext()
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ContextOptions.Negotiate);
return oPrincipalContext;
}
I hope this code will be of use for you.

Related

Validate user credentials and read associated groups for a user

Currently, I am validating the credentials of an Active Directory user with the help of this code:
string _groups = "";
bool _isAuthSuccess=true;
List<string> user_groups = new List<string>();
try
{
using (PrincipalContext _loginContext = new PrincipalContext(ContextType.Domain, "domainname"))
{
_message += "calling ValidateCredentials";
_isAuthSuccess = _loginContext.ValidateCredentials(model.Email, model.Password);
if(_isAuthSuccess)
{
_message += "calling FindByIdentity";
var user = UserPrincipal.FindByIdentity(_loginContext, model.Email);
if (user != null)
{
// get the user's groups
_message += "calling GetAuthorizationGroups";
var groups = user.GetAuthorizationGroups();
foreach (GroupPrincipal group in groups)
{
// save those groups to session for further processing after login
if ((bool)group.IsSecurityGroup)
{
user_groups.Add(group.Name);
}
}
}
_groups = string.Join(",", user_groups);
}
else
{
_message += "_isAuthSuccess is false";
}
}
}
catch (PrincipalServerDownException)
{
_message += "Error at logon validatyion as server is down ";
}
catch(Exception ex)
{
_message += "Exception : "+ex.Message;
}
The bool flag is returning the status the user credentials are valid or not. Now i wanted to fetch the list of Active Directory UserGroups the user is a member of. I found that the method GetAuthorizationGroups will return the list of user groups. But I am struggling to relate these 2 methods as there is no way to call _loginCOntext.GetAuthorizationGroups()
So how can efficiently handle these 2 cases together
validate credentials and
get the list of user groups together.
The GetAuthorizationGroups() method can only be called on a UserPricipal, so you need to get one for the user. That's easy to do with the UserPrincipal.FindByIdentity method (while reusing the _loginContext object you already have):
var user = UserPrincipal.FindByIdentity(_loginContext, user_name);
var groups = user.GetAuthorizationGroups();
Update: To avoid the "Multiple connections" error, try using different instances of PrincipalContext for each operation. At the end of the using, it should disconnect the connection with the server and allow you to start a new one with different credentials without problem.
using (PrincipalContext _loginContext = new PrincipalContext(ContextType.Domain, "domainname"))
{
_message += "calling ValidateCredentials";
_isAuthSuccess = _loginContext.ValidateCredentials(model.Email, model.Password);
}
using (PrincipalContext _loginContext = new PrincipalContext(ContextType.Domain, "domainname", "username", "password"))
{
if(_isAuthSuccess)
{
...
}
else
{
_message += "_isAuthSuccess is false";
}
}

Find if user belongs to group

I want to find if the user belongs to an AD group. Can you advise how I can add that functionality using the following code?
I ask the user to enter their username and password (through a form), so not using the windows credentials. With the below code I am able to validate the user, by passing the username, and password. How can I build on the code to check if user exists in the AD Group. Is there another way to do this? Please advice
DirectoryEntry adsEntry = new DirectoryEntry("domain", userid, password);
DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry);
try {
SearchResult adsSearchResult = adsSearcher.FindOne();
context.Session.Timeout = 2;
context.Session["ValidatedLoginID"] = userid;
user.Verified = true;
adsEntry.Close();
} catch ( Exception ex ) {
// Failed to authenticate. Most likely it is caused by unknown user
// id or bad strPassword.
user.error = ex.Message;
adsEntry.Close();
}
You can use the below code:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "DOMAINNAME");
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
if(user != null)
{
// check if user is member of that group
if (user.IsMemberOf(group))
{
// do something.....
}
}
Also look at: How to check if a user belongs to an AD group?
Here is how I solved this :
DirectoryEntry adsEntry = new DirectoryEntry("domain", userid, password);
DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry);
adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person)(sAMAccountName=" + userid + "))";
try
{
SearchResult adsSearchResult = adsSearcher.FindOne();
string propertyName = "memberOf";
ResultPropertyValueCollection rpvcResult = adsSearchResult.Properties[propertyName];
foreach (Object PropertyValue in rpvcResult)
{
if (PropertyValue.ToString() == "Group Name")
{
user.Verified = true;
user.FullName = GetFullName(userid);
adsEntry.Close();
} else
{
user.Verified = false;
user.error = "You do not belong to the Group so you cannot do this function";
}
}
} catch (Exception ex)
{
user.error = "Please check your username and password credentials";
adsEntry.Close();
}

How the PrincipalContext works

I need to let users bind there accounts to the Active Directory. This means that admin needs a GUI where he/she can write a Active Directory account like this : MyDomain\MyName and then get a validation if the users exists before save.
Im using this code to validate the name :
public static bool CheckActiveDirectoryAccount(string account)
{
string ADServer = null;
string ADDomain = null;
string ADUserName = null;
string ADUserPassword = null;
SetADSettings(out ADServer, out ADDomain, out ADUserName, out ADUserPassword);
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, ADServer, ADUserName, ADUserPassword))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, account))
{
if(user != null)
return true;
else
return false;
}
}
}
The problem with this code is that there seems to be no way to check the user for a specific domain? Instead I have to input the server, if I try to input the domain instead there will be exception(Server not found).
How do I let the admin enter domain and username of a AD account and then check it against the AD?
I am able to pass the domain into the principalcontext without issue, I'm not passing in the server. I would expect this to work for you.
public static bool CheckActiveDirectoryAccount(string account, string domain)
{
using (var pc = new PrincipalContext(ContextType.Domain, domain))
{
// Find a user
UserPrincipal user = UserPrincipal.FindByIdentity(pc, account);
if (user == null)
return false;
return true;
}
}
I have noticed poor performance when passing in the NetBIOS domain name, though it does work. As a result I pass in the DNS domain name whenever possible.
I ended up with this :
public static string CheckActiveDirectoryAccount(string account)
{
UserPrincipal user;
PrincipalContext context;
List<string> userPrincipalNameList;
string ADServer = null;
string ADUserName = null;
string ADUserPassword = null;
string userAccount;
account = account.ToLower();
GetADSettings(out ADServer, out ADUserName, out ADUserPassword);
if (ADUserName.Length > 0)
context = new PrincipalContext(ContextType.Domain, ADServer, null, ADUserName, ADUserPassword);
else
context = new PrincipalContext(ContextType.Domain, ADServer);
using (context)
{
if((user = UserPrincipal.FindByIdentity(context, account)) == null)
{
if(account.Contains("\\"))
{
userPrincipalNameList = user.UserPrincipalName.Split('\\').ToList();
if (userPrincipalNameList.Count > 0)
user = UserPrincipal.FindByIdentity(context, userPrincipalNameList[0]);
}
}
if (user != null)
{
using (user)
{
userPrincipalNameList = user.UserPrincipalName.Split('#').ToList();
userAccount = userPrincipalNameList.First();
if (userPrincipalNameList.Count > 1)
userAccount = userPrincipalNameList.Last() + "\\" + userAccount;
if (user != null)
return userAccount.ToLower();
}
}
}
return string.Empty;
}

create AD-User in c#

I am trying to create a new AD-User with this code:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "Domain", "ou=some_ou, dc=Mydomain");
UserPrincipal user = new UserPrincipal(ctx, account, passwd, true);
user.GivenName = Givenname;
user.Surname = Surname;
user.DisplayName = Displayname;
user.UserPrincipalName = account + "#Domain";
user.Save();
The User is created without error. But I also have to set properties like Address etc, so the code continues with:
string distname = user.DistinguishedName;
DirectoryEntry duser = new DirectoryEntry(distname);
try
{
duser.Properties["company"].Value = "Company";
}
catch (Exception e)
{
}
Now I am getting
Error: System.Exception._COMPlusExceptionCode -532459699
The string distname shows the correct distinguished name.
I am not 100% sure what is causing your problem but one thing that may make things easier on you and may clear up some errors due to you improperly using both DirectoryServices and DirectoryServices.AccountManagement at the same time is creating a new class that includes the company attribute.
Its actually not that hard to do.
[DirectoryObjectClass("user")]
[DirectoryRdnPrefix("CN")]
public class UserPrincipalEx : UserPrincipal
{
public UserPrincipalEx(PrincipalContext context) : base(context) { }
public UserPrincipalEx(PrincipalContext context, string samAccountName, string password, bool enabled)
: base(context, samAccountName, password, enabled)
{
}
[DirectoryProperty("company")]
public string Company
{
get
{
if (ExtensionGet("company").Length != 1)
return null;
return (string)ExtensionGet("company")[0];
}
set { this.ExtensionSet("company", value); }
}
}
You can then just modify your code to
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "Domain", "ou=some_ou, dc=Mydomain");
UserPrincipalEx user = new UserPrincipalEx(ctx, account, passwd, true);
user.GivenName = Givenname;
user.Surname = Surname;
user.DisplayName = Displayname;
user.UserPrincipalName = account + "#Domain";
user.Company = "Company";
user.Save();
My hunch is you are having some kind of interaction with the two methods of interfacing with active directory, if you switch to a single interface your problem may just go away.
For DirectoryEntry, you have to specify the protocol (LDAP, GC, WinNT, ...). So you would have to do:
DirectoryEntry duser = new DirectoryEntry("LDAP://" + distname);
Note that the protocol is case sensitive, LDAP has to be all caps.
I see you are using credentials in the UserPrincipal,
Did you forgot to use them when creating your DirectoryEntry?
Also, you need to add "LDAP://" before you server name
Try something like :
DirectoryEntry duser = new DirectoryEntry("LDAP://" + distname);
duser.Username = account;
duser.Password = passwd;
duser.AuthenticationType = AuthenticationTypes.Secure;

Remove user account from administrators group on remote machine using C# and AccountManagment namespace

I have the code:
public bool RemoveUserFromAdministratorsGroup(UserPrincipal oUserPrincipal, string computer)
{
try
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine, computer, null, ContextOptions.Negotiate, _sServiceUser, _sServicePassword);
GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, "Administrators");
oGroupPrincipal.Members.Remove(oUserPrincipal);
oGroupPrincipal.Save();
return true;
}
catch
{
return false;
}
}
It is worked without any excaption. But when i run my app again i see this user in my listview. So, the user wasn't removed.
I have solved the issue without AccountManagment namespace.
public bool RemoveUserFromAdminGroup(string computerName, string user)
{
try
{
var de = new DirectoryEntry("WinNT://" + computerName);
var objGroup = de.Children.Find(Settings.AdministratorsGroup, "group");
foreach (object member in (IEnumerable)objGroup.Invoke("Members"))
{
using (var memberEntry = new DirectoryEntry(member))
if (memberEntry.Name == user)
objGroup.Invoke("Remove", new[] {memberEntry.Path});
}
objGroup.CommitChanges();
objGroup.Dispose();
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return false;
}
}
The below solution is for deleting the user with the help of Directory Service ...
using System.DirectoryServices
private DeleteUserFromActiveDirectory(DataRow in_Gebruiker)
{
DirectoryEntry AD = new DirectoryEntry(strPathActiveDirectory ,
strUsername, strPassword)
DirectoryEntry NewUser =
AD.Children.Find("CN=TheUserName", "User");
AD.Children.Remove(NewUser);
AD.CommitChanges();
AD.Close();
}
I don't know what is exactly your problem but coding this way :
try
{
PrincipalContext context = new PrincipalContext(ContextType.Domain, "WM2008R2ENT:389", "dc=dom,dc=fr", "jpb", "passwd");
/* Retreive a user principal
*/
UserPrincipal user = UserPrincipal.FindByIdentity(context, "user1");
/* Retreive a group principal
*/
GroupPrincipal adminGroup = GroupPrincipal.FindByIdentity(context, #"dom\Administrateurs");
foreach (Principal p in adminGroup.Members)
{
Console.WriteLine(p.Name);
}
adminGroup.Members.Remove(user);
adminGroup.Save();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Give me the following exception :
Information about the domain could not be retrieved (1355)
Digging a bit arround that show me that I was running my code on a computer that was not on the target domain. When I run the same code from the server itself it works. It seems that the machine running this code must at least contact the DNS of the target domain.

Categories

Resources