Help I am trying to remove domain users from local groups and every time I try to get the local groups for a domain user the collection is empty. How can I remove a domain user from any local groups on my machine. Below is the code I am using and it works fine for local users, but as stated above on domain users it says they have no local groups. I know for a fact that the domain user is in the Users and IIS_USRS groups on the local machine.
using (PrincipalContext localContext = new PrincipalContext(ContextType.Machine))
{
try
{
foreach (GroupPrincipal principal in user.GetGroups(localContext))
{
principal.Members.Remove(user);
principal.Save(localContext);
principal.Dispose();
}
}
In order to get this to work I ended up having to do.
using (PrincipalContext localContext = new PrincipalContext(ContextType.Machine))
{
try
{
foreach (string g in groups)
{
using (GroupPrincipal localGroup = GroupPrincipal.FindByIdentity(localContext, IdentityType.Name, g))
{
foreach (Principal groupUser in localGroup.GetMembers().Where(groupUser => user.Name.Equals(groupUser.Name)))
{
localGroup.Members.Remove(groupUser);
localGroup.Save();
}
}
}
}
Related
So I was looking on SO for the answer to this question and it seemed as everything I came across was for Active Directory or something similar. There were no examples for this for the local machine.
Basically the goal here is to get all the user groups in the system. The same ones you'd see if you launch Computer Management, traverse to System Tools > Local Users and Groups > Groups
using (PrincipalContext context = new PrincipalContext(ContextType.Machine))
{
using (GroupPrincipal groupPrincipal = new GroupPrincipal(context))
{
groupPrincipal.Name = "*";
PrincipalSearcher principalSearcher = new PrincipalSearcher();
principalSearcher.QueryFilter = groupPrincipal;
PrincipalSearchResult<Principal> groupList = principalSearcher.FindAll();
foreach (Principal group in groupList)
{
// Do something with info
}
}
}
In C#, I'm trying to authenticate a user against a group in ActiveDirectory. The code below works fine for users within our domain, but we also have users in other countries that log in to our vpn and need to access my program. The code below crashes when they attempt to run it. I've tried everything and I just can't figure this out.
var principalContext = new PrincipalContext(ContextType.Domain)
var groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, IdentityType.Name, "myGroup")
var members = groupPrincipal.GetMembers(true).ToList()
var isMember = members.Any(m => m.Guid == userPrincipal.Guid)
How can I test to see if a user is part of an Active Directory group that is outside of our domain?
Thankx
Are the VPN users authenticating against the domain? Also, how are you getting the userPrincipal?
Here's some code that tackles the problem from the other side. It's a little older but I used it to verify users could run a small program of mine.
var userGroups = WindowsIdentity.GetCurrent().Groups;
foreach (var domainGroup in userGroups)
{
var group = domainGroup.Translate(typeof(NTAccount));
if (group.Value == "domain\\myGroup")
{
isMember = true;
}
}
Of course you can translate that into the appropriate linq statement if need be.
I have a situation where I need to confirm that I am able to get write access to a particular folder on the network. So firstly I check is the user or one of the groups it is part of (domain groups) one of the identities in the access rules. If that is not the case then I need to go through and check all the local groups on the remote machine, to see if my user or one of the user's groups is a member of the local groups on the machine. For example on this particular machine I am a member of BUILTIN\Administrators, which means I do have write access to the given folder. However I am not clear how to get that local group from the remote machine to check if I have write access.
In the code below, when I try and use GroupPrincipal.FindByIdentity, it gives an exception "The binding handle is invalid." I'm not clear what is invalid. If I just try and verify my username and password (a domain username), by using ctx.ValidateCredentials(UserName, Password), it gives exactly the same error.
If I give the machine name as just "pvr-pc", it says it cannot find the name on the network, but "\\pvr-pc" does get past that problem fine.
The same code correctly discovers that I one of the groups I am part of is a group in BUILTIN\Administrators on my local machine, so it's something about the network access that is the problem.
Does anyone have any ideas what I've done wrong?
The code looks like this:
using (WindowsIdentity identity = GetUserIdentity())
{
if (identity != null)
{
try
{
FileInfo fi = new FileInfo(#"\\pvr-pc\c\installers\");
AuthorizationRuleCollection acl = fi.GetAccessControl().GetAccessRules
(true, true, typeof (SecurityIdentifier));
var rules = acl.Cast<FileSystemAccessRule>();
List<string> sids = new List<string>();
sids.Add(identity.User.Value);
sids.AddRange(identity.Groups.Select(identityReference => identityReference.Value));
// check for a direct user match
var matches = from r in rules where sids.Contains(r.IdentityReference.Value) select r;
foreach (FileSystemAccessRule accessRule in matches)
{
// apply rules
}
foreach (FileSystemAccessRule rule in rules)
{
// if it is built in, try and get the group
var groupDetail = rule.IdentityReference.Translate(typeof (NTAccount));
if (!groupDetail.Value.StartsWith("BUILTIN\\")) continue;
PrincipalContext ctx = new PrincipalContext(ContextType.Machine, #"\\pvr-pc", null,
ContextOptions.Negotiate, UserName, Password);
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Sid,
rule.IdentityReference.Value);
if (grp != null)
{
//// find out if we are a member of the group
var isInGroup = (from g in grp.GetMembers(true)
where sids.Contains(g.Sid.ToString())
select g).Any();
if (isInGroup)
{
// apply rules
}
}
}
}
catch (Exception ex)
{
}
Thanks,
Stefan
Would it not be easier to simply read or write to the folder and then catch the Exception; then inform the user s/he has no access.
I’m having an issue with integrating my ASP.NET web service with an Active Directory setup, and using it to authentication users and check with AD groups they are a member of and if they have permissions to use my custom application.
My custom application has its own permissions, and the administrators configure Active Directory groups that are allow to use the custom application.
The issue I’m having is when a user from a different Trusted AD forest, with full two way trust, attempts to login I can’t get a list of his groups from the AD server my ASP.NET web services communicates with. The ASP.NET web service only has access to the AD server (AD Main), not the trust AD controller (AD Secondary).
The user is a member of the (AD Secondary) domain, and I can authenticate that user against the (AD Main) domain, but I can’t get a list of groups from the (AD Main) domain when the user is in the (AD Secondary) domain.
I’ve tried this code.
StringCollection groupids = new StringCollection();
try
{
DirectoryLibrary dirLib = new DirectoryLibrary();
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://" + domain,username, password);
if (directoryEntry != null)
{
//Enum the properties so we can see what is in them
foreach (string propname in directoryEntry.Properties.PropertyNames)
{
Debug.WriteLine(propname);
}
object obGroups = directoryEntry.Invoke("Groups");
foreach (object ob in (IEnumerable)obGroups)
{
// Create object for each group.
DirectoryEntry obGpEntry = new DirectoryEntry(ob);
groupids.Add(obGpEntry.NativeGuid);
}
}
}
catch (DirectoryServicesCOMException ex) { throw ex; }
I’ve tried to move away from the DirectoryEntry object to, something like this.
List<GroupPrincipal> result = new List<GroupPrincipal>();
StringCollection groupids = new StringCollection();
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain, domain, userName, password);
// find your user
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);
// if found - grab its groups
if (user != null)
{
PrincipalSearchResult<Principal> groups = user.GetGroups();
// iterate over all groups
foreach (Principal p in groups)
{
// make sure to add only group principals
if (p is GroupPrincipal)
{
groupids.Add(p.DisplayName);
}
}
}
But, I don’t get the user and I can’t get a list of the groups for that user in the other domain. Any help would be appreciated.
This appears to be a great use case for the AD derived attribute memberOf. With the DirectoryEntry directoryEntry object, you can enumerate what groups a user belongs to.
foreach (object group in directoryEntry.Properties["memberOf"])
{
DirectoryEntry obGpEntry = New DirectoryEntry("LDAP://" + (String)group);
groupids.Add(obGpEntry.NativeGuid);
}
It's also likely that you could use the first code segment if you prefixed ob with "LDAP://"
I think you have to connect to the remote AD and get the data you want.
I wrote a replication once, where i replicate from many AD's
Some code out of it:
Public Function GetDirectoryEntry() As Object
If InStr(1, m_sLdapPath, "DC=") > 0 Then
Dim directory_service As New PrincipalContext(ContextType.Domain, m_sDomain, m_sLdapPath)
Return directory_service
Else
Dim directory_service As New PrincipalContext(ContextType.Machine, m_sDomain, m_sLdapPath)
Return directory_service
End If
End Function
Public Function GetUserList() As PrincipalSearchResult(Of Principal)
Dim directory_service As PrincipalContext = CType(GetDirectoryEntry(), PrincipalContext)
Dim directory_user As New UserPrincipal(directory_service)
Dim directory_userlist As New PrincipalSearcher(directory_user)
directory_userlist.QueryFilter = directory_user
Return directory_userlist.FindAll
End Function
Public Function GetGroupList() As PrincipalSearchResult(Of Principal)
Dim directory_service As PrincipalContext = CType(GetDirectoryEntry(), PrincipalContext)
Dim directory_group As New GroupPrincipal(directory_service)
Dim directory_grouplist As New PrincipalSearcher(directory_group)
directory_grouplist.QueryFilter = directory_group
Return directory_grouplist.FindAll
End Function
I know this is not exactly what you need, but this shows how to connect and fetch data from any AD.
In my case I get a userlist, grouplist or whatever and then work with those collections.
Dim l_oGroupList As Object = oDirectory.GetGroupList()
For Each l_oGroup In l_oGroupList
If l_oGroup.Members.Count > 0 Then
If l_oGroup.Members.Contains(directory_service, IdentityType.UserPrincipalName, Username) Then
' he is part of the group
End If
End If
Next
I hope this helps a bit to solve the problem...
How can I get a list of users from active directory? Is there a way to pull username, firstname, lastname? I saw a similar post where this was used:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
I have never done anything with active directory so I am completely lost. Any help would be greatly appreciated!
If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.
Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory
The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.
There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.
For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
}
}
}
Console.ReadLine();
Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.
If you want to filter y active accounts add this to Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
after the first using. Then add
searcher.QueryFilter = userPrin;
before the find all. And that should get you the active ones.
PrincipalContext for browsing the AD is ridiculously slow (only use it for .ValidateCredentials, see below), use DirectoryEntry instead and .PropertiesToLoad() so you only pay for what you need.
Filters and syntax here:
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Attributes here:
https://learn.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://{Domain}"))
{
using (var searcher = new DirectorySearcher(root))
{
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={username}))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
{
// ... iterate over all the groups the user is a member of
}
}
}
Clean, simple, fast. No magic, no half-documented calls to .RefreshCache to grab the tokenGroups or to .Bind or .NativeObject in a try/catch to validate credentials.
For authenticating the user:
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(username, password);
}
Certainly the credit goes to #Harvey Kwok here, but I just wanted to add this example because in my case I wanted to get an actual List of UserPrincipals. It's probably more efficient to filter this query upfront, but in my small environment, it's just easier to pull everything and then filter as needed later from my list.
Depending on what you need, you may not need to cast to DirectoryEntry, but some properties are not available from UserPrincipal.
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
{
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
{
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
}
}
Include the System.DirectoryServices.dll, then use the code below:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName == "User")
{
userNames += child.Name + Environment.NewLine ;
}
}
MessageBox.Show(userNames);