Removing a User From an Active Directory Group - c#

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

Related

the execution time in this method

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

C# Issue with manipulating ActiveDirectory users

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 :)

Get Principal Context object using LDAP path

I am working on a module where I need to fetch members of an Active Directory group. This functionality already exists in the project but it was built for .Net3.5. The same is not working for .Net4.5. After some googling I found that I need to use "Principal Context" object to get the Directory entry object.
The problem here is, I need to do the testing in Test AD, which is different from my production AD.
The old way I used was allowing me to specify the test AD server path,
DirectoryEntry entry = new DirectoryEntry(ADLdapPath, ADAdminUser, ADAdminPassword, AuthenticationTypes.Secure);
Can anyone please help me find a way to specify LDAP path(AD server path) while creating "Principal Context" so that I can do the testing in Test environment.
I've used the following helper (modified) which is part of my AD tool belt to create PrincipalContext for working with AD. This should get you started. Modify it to suit your needs. Hope it helps.
public class ADHelper {
public static PrincipalContext CreatePrincipalContext(string domain = null) {
string container = null;
if (IsNullOrWhiteSpace(domain)) {
domain = GetCurrentDnsSuffix();
if (domain != null && domain.EndsWith(".com", StringComparison.InvariantCultureIgnoreCase)) {
container = GetContainers(domain);
} else {
domain = null;
}
}
var hostName = GetHostName();
if (IsNullOrWhiteSpace(domain)) {
domain = hostName;
}
ContextType contextType;
if (domain.Equals(hostName, StringComparison.InvariantCultureIgnoreCase) &&
domain.Equals(Environment.MachineName, StringComparison.InvariantCultureIgnoreCase)) {
contextType = ContextType.Machine;
} else {
contextType = ContextType.Domain;
}
PrincipalContext principalContext = null;
if (contextType == ContextType.Machine) {
principalContext = new PrincipalContext(contextType, domain);
} else {
principalContext = new PrincipalContext(contextType, domain, container, Constants.LDAPUser, Constants.LDAPPassword);
}
return principalContext;
}
public static string GetCurrentDnsSuffix() {
string dnsHostName = null;
if (NetworkInterface.GetIsNetworkAvailable()) {
var nics = NetworkInterface.GetAllNetworkInterfaces()
.Where(ni => ni.OperationalStatus == OperationalStatus.Up);
foreach (var ni in nics) {
var networkConfiguration = ni.GetIPProperties();
var dnsSuffix = networkConfiguration.DnsSuffix;
if (dnsSuffix != null) {
dnsHostName = dnsSuffix;
break;
}
var address = networkConfiguration.DnsAddresses.FirstOrDefault();
if (address != null) {
try {
var dnsHost = Dns.GetHostEntry(address.ToString());
dnsHostName = dnsHost.HostName;
} catch (System.Net.Sockets.SocketException e) {
traceError(e);
} catch (Exception e) {
traceError(e);
}
}
}
}
return dnsHostName;
}
private static string GetContainers(string ADServer) {
string[] LDAPDC = ADServer.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < LDAPDC.GetUpperBound(0) + 1; i++) {
LDAPDC[i] = string.Format("DC={0}", LDAPDC[i]);
}
String ldapdomain = Join(",", LDAPDC);
return ldapdomain;
}
public static string GetHostName() {
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
return ipProperties.HostName;
}
}
I can then use it in something like this
public static List<string> GetAllUserNames(string domain = null) {
List<string> userNames = new List<string>();
using (var principalContext = createPrincipalContext(domain)) {
//Get a list of user names in MyDomain that match filter
using (UserPrincipal userPrincipal = new UserPrincipal(principalContext)) {
using (PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal)) {
var results = principalSearcher
.FindAll()
.Where(c =>
(c is UserPrincipal) &&
(c as UserPrincipal).Enabled.GetValueOrDefault(false) &&
!string.IsNullOrEmpty(c.DisplayName)
);
foreach (UserPrincipal p in results) {
var temp = p.StructuralObjectClass;
string value = string.Format("{0} ({1})", p.DisplayName, p.EmailAddress ?? Join("\\", p.Context.Name, p.SamAccountName));
userNames.Add(value);
}
}
}
}
return userNames;
}

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.

Get members of an Active Directory group recursively, i.e. including subgroups

Given a group like this in Active Directory:
MainGroup
GroupA
User1
User2
GroupB
User3
User4
I can easily determine if User3 is member of MainGroup or any of its subgroups with code like this:
using System;
using System.DirectoryServices;
static class Program {
static void Main() {
DirectoryEntry user = new DirectoryEntry("LDAP://CN=User3,DC=X,DC=y");
string filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=MainGroup,DC=X,DC=y)";
DirectorySearcher searcher = new DirectorySearcher(user, filter);
searcher.SearchScope = SearchScope.Subtree;
var r = searcher.FindOne();
bool isMember = (r != null);
}
}
I would like to know if there is a similar way to get all the users that are member of a group or any of its subgroups, i.e. in the example for MainGroup get User1, User2, User3 and User4.
The obvious way of getting all the users is to recursively query each subgroup, but I was wondering if there is an easier way to do it.
Using the same approach with the memberOf:1.2.840.113556.1.4.1941: filter, but using the domain root instead of the user as a search base is not feasible, as the query takes too long (probably it computes all the group memberships recursively for all users in the domain and checks if they are member of the given group).
Which is the best way to get all members of a group, including its subgroups?
Just in case this might benefit someone else: here is the solution I ended up with. It is just a recursive search, with some extra checks to avoid checking the same group or user twice, e.g. if groupA is member of groupB and groupB is member of groupA or a user is member of more than one group.
using System;
using System.DirectoryServices;
using System.Collections.Generic;
static class Program {
static IEnumerable<SearchResult> GetMembers(DirectoryEntry searchRoot, string groupDn, string objectClass) {
using (DirectorySearcher searcher = new DirectorySearcher(searchRoot)) {
searcher.Filter = "(&(objectClass=" + objectClass + ")(memberOf=" + groupDn + "))";
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.AddRange(new string[] {
"objectGUID",
"sAMAccountName",
"distinguishedName"});
searcher.Sort = new SortOption("sAMAccountName", SortDirection.Ascending);
searcher.PageSize = 1000;
searcher.SizeLimit = 0;
foreach (SearchResult result in searcher.FindAll()) {
yield return result;
}
}
}
static IEnumerable<SearchResult> GetUsersRecursively(DirectoryEntry searchRoot, string groupDn) {
List<string> searchedGroups = new List<string>();
List<string> searchedUsers = new List<string>();
return GetUsersRecursively(searchRoot, groupDn, searchedGroups, searchedUsers);
}
static IEnumerable<SearchResult> GetUsersRecursively(
DirectoryEntry searchRoot,
string groupDn,
List<string> searchedGroups,
List<string> searchedUsers) {
foreach (var subGroup in GetMembers(searchRoot, groupDn, "group")) {
string subGroupName = ((string)subGroup.Properties["sAMAccountName"][0]).ToUpperInvariant();
if (searchedGroups.Contains(subGroupName)) {
continue;
}
searchedGroups.Add(subGroupName);
string subGroupDn = ((string)subGroup.Properties["distinguishedName"][0]);
foreach (var user in GetUsersRecursively(searchRoot, subGroupDn, searchedGroups, searchedUsers)) {
yield return user;
}
}
foreach (var user in GetMembers(searchRoot, groupDn, "user")) {
string userName = ((string)user.Properties["sAMAccountName"][0]).ToUpperInvariant();
if (searchedUsers.Contains(userName)) {
continue;
}
searchedUsers.Add(userName);
yield return user;
}
}
static void Main(string[] args) {
using (DirectoryEntry searchRoot = new DirectoryEntry("LDAP://DC=x,DC=y")) {
foreach (var user in GetUsersRecursively(searchRoot, "CN=MainGroup,DC=x,DC=y")) {
Console.WriteLine((string)user.Properties["sAMAccountName"][0]);
}
}
}
}
static List<SearchResult> ad_find_all_members(string a_sSearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
{
using (DirectoryEntry de = new DirectoryEntry(a_sSearchRoot))
return ad_find_all_members(de, a_sGroupDN, a_asPropsToLoad);
}
static List<SearchResult> ad_find_all_members(DirectoryEntry a_SearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
{
string sDN = "distinguishedName";
string sOC = "objectClass";
string sOC_GROUP = "group";
string[] asPropsToLoad = a_asPropsToLoad;
Array.Sort<string>(asPropsToLoad);
if (Array.BinarySearch<string>(asPropsToLoad, sDN) < 0)
{
Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
asPropsToLoad[asPropsToLoad.Length-1] = sDN;
}
if (Array.BinarySearch<string>(asPropsToLoad, sOC) < 0)
{
Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
asPropsToLoad[asPropsToLoad.Length-1] = sOC;
}
List<SearchResult> lsr = new List<SearchResult>();
using (DirectorySearcher ds = new DirectorySearcher(a_SearchRoot))
{
ds.Filter = "(&(|(objectClass=group)(objectClass=user))(memberOf=" + a_sGroupDN + "))";
//ds.PropertiesToLoad.Clear();
ds.PropertiesToLoad.AddRange(asPropsToLoad);
//ds.PageSize = 1000;
//ds.SizeLimit = 0;
foreach (SearchResult sr in ds.FindAll())
lsr.Add(sr);
}
for(int i=0;i<lsr.Count;i++)
if (lsr[i].Properties.Contains(sOC) && lsr[i].Properties[sOC].Contains(sOC_GROUP))
lsr.AddRange(ad_find_all_members(a_SearchRoot, (string)lsr[i].Properties[sDN][0], asPropsToLoad));
return lsr;
}
static void Main(string[] args)
{
foreach (var sr in ad_find_all_members("LDAP://DC=your-domain,DC=com", "CN=your-group-name,OU=your-group-ou,DC=your-domain,DC=com", new string[] { "sAMAccountName" }))
Console.WriteLine((string)sr.Properties["distinguishedName"][0] + " : " + (string)sr.Properties["sAMAccountName"][0]);
}
To get the members recursively take this (the trick is GetMembers(true) instead of false which is the default value):
private List<string> GetGroupMembers(string groupName)
{
var members = new List<string>();
try
{
using (var pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
{
var gp = GroupPrincipal.FindByIdentity(pc, groupName);
if (gp == null) return members;
foreach (Principal p in gp.GetMembers(true))
members.Add(p.Name);
members.Sort();
}
}
catch (Exception)
{
return new List<string>();
}
return members;
}
I also wanted to know if a user or a computer is in an ActiveDirectory group. And this worked for me also with nested groups (it is part of a WebService but you can use the code also in a standalone application):
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
[HttpGet]
[Route("IsUserInGroup")]
public HttpResponseMessage IsUserInGroup(string userName, string groupName)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
{
var gp = GroupPrincipal.FindByIdentity(pc, groupName);
var up = UserPrincipal.FindByIdentity(pc, userName);
if (gp == null)
{
response.Content = Common.ConvertToJsonContent($"Group '{groupName}' not found in Active Directory");
response.StatusCode = HttpStatusCode.NotFound;
return response;
}
if (up == null)
{
response.Content = Common.ConvertToJsonContent($"User '{userName}' not found in Active Directory");
response.StatusCode = HttpStatusCode.NotFound;
return response;
}
DirectoryEntry user = new DirectoryEntry($"LDAP://{up.DistinguishedName}");
DirectorySearcher mySearcher = new DirectorySearcher(user)
{
SearchScope = System.DirectoryServices.SearchScope.Subtree,
Filter = $"(memberOf:1.2.840.113556.1.4.1941:={gp.DistinguishedName})"
};
SearchResult result = mySearcher.FindOne();
response.StatusCode = HttpStatusCode.OK;
response.Content = Common.ConvertToJsonContent(result != null);
}
}
catch (Exception ex)
{
response.StatusCode = HttpStatusCode.BadRequest;
response.Content = Common.ConvertToJsonContent($"{MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
return response;
}
[HttpGet]
[Route("IsComputerInGroup")]
public HttpResponseMessage IsComputerInGroup(string computerName, string groupName)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
try
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
{
var gp = GroupPrincipal.FindByIdentity(pc, groupName);
var cp = ComputerPrincipal.FindByIdentity(pc, computerName);
if (gp == null)
{
response.Content = Common.ConvertToJsonContent($"Group '{groupName}' not found in Active Directory");
response.StatusCode = HttpStatusCode.NotFound;
return response;
}
if (cp == null)
{
response.Content = Common.ConvertToJsonContent($"Computer '{computerName}' not found in Active Directory");
response.StatusCode = HttpStatusCode.NotFound;
return response;
}
DirectoryEntry computer = new DirectoryEntry($"LDAP://{cp.DistinguishedName}");
DirectorySearcher mySearcher = new DirectorySearcher(computer)
{
SearchScope = System.DirectoryServices.SearchScope.Subtree,
Filter = $"(memberOf:1.2.840.113556.1.4.1941:={gp.DistinguishedName})"
};
SearchResult result = mySearcher.FindOne();
response.StatusCode = HttpStatusCode.OK;
response.Content = Common.ConvertToJsonContent(result != null);
}
}
catch (Exception ex)
{
response.StatusCode = HttpStatusCode.BadRequest;
response.Content = Common.ConvertToJsonContent($"{MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
return response;
}

Categories

Resources