Validate user credentials and read associated groups for a user - c#

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";
}
}

Related

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();
}

Using C#'s Computer Principal in Active Directory to fill list box with computers based on User Selection

My C# application communicates with the active directory and is able to provide me when i select a user i can see the groups he is a member of and if i select the group i am able to see the users in the group. Now the new requirement i want to see the computers each user is associated with.
private void FillComputers()
{
computers.Items.Clear();
PrincipalContext PrincipalContext1 = new PrincipalContext(ContextType.Domain);
ComputerPrincipal cp = new ComputerPrincipal(PrincipalContext1);
PrincipalSearcher search = new PrincipalSearcher(cp);
foreach (var cpn in search.FindAll())
{
computers.Items.Add(cpn.SamAccountName);
}
}
Gives me all the computers how do i pass the selected user as a parameter to the FillComputers above??? Thanks in Advance for all your suggestions.
For example when i have users and i had to find out the groups they were memebers of i did this
The below fills the listbox with all users:
private void filluser()
{
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for any UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach (var found in srch.FindAll())
{
try
{
// do whatever here
UserPrincipal foundUser = found as UserPrincipal;
if (foundUser != null)
{
foundUser.IsAccountLockedOut();
user.Items.Add(foundUser.GivenName + " " + foundUser.Surname + " " + "[" + foundUser.SamAccountName + "]");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
To Fill another list box with the groups the users were members of I did this
private void fillmembersof()
{
Groups.Items.Clear();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
String input = user.Text;
string username = input.Split(new char[] { '[', ']' })[1];
// define a "query-by-example" principal - here, we search for any UserPrincipal
UserPrincipal usr = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, username);
if (usr != null)
{
foreach (var group in usr.GetGroups())
{
Groups.Items.Add(group.Name);
}
usr.Dispose();
}
ctx.Dispose();
}
Than to fill the above text box based on users selected i did this:
private void user_SelectedIndexChanged(Object sender, EventArgs e)
{
fillmembersof();
**FillComputers();** -> i want to be able to do the same for computers the user has access to?????
Thanks you in advance for all your valuable suggestions!!!!

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;
}

Timeout issue When Using Active Directory

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.

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