Get Primary Group From UserPrincipal C# - c#

I want to locate the Primary Group from the code below
I can get all the Groups for a user, but which one is the primary group?
string primaryGroupName = String.Empty;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "userName"))
{
foreach (Principal p in user.GetGroups())
{
WriteLog("PrimaryGroup Name(s)???:");
WriteLog(p.Name);
primaryGroupName = p.Name;
}
}
}
Whats returned from the code above is...
Domain Users
Administrators
Schema Admins
Enterprise Admins
Domain Admins
..and a few more
What is the Primary Group?

You have the right idea: The primaryGroupID holds the RID (Relative Identifier) of the group that is the primary group. The RID is the last set of numbers in the SID. The rest of the SID identifies the domain. So you can figure out the SID of the group using the SID of the user and the primaryGroupID.
I wrote a couple articles about this. One called What makes a member a member?, with a section describing the primary group. But also an article called Finding all of a user’s groups where I shared some code to find the primary group. I have found that using DirectoryEntry directly is always faster than using UserPrincipal/GroupPrincipal, so that's what my examples use:
Here is the method:
private static string GetUserPrimaryGroup(DirectoryEntry de) {
de.RefreshCache(new[] {"primaryGroupID", "objectSid"});
//Get the user's SID as a string
var sid = new SecurityIdentifier((byte[])de.Properties["objectSid"].Value, 0).ToString();
//Replace the RID portion of the user's SID with the primaryGroupId
//so we're left with the group's SID
sid = sid.Remove(sid.LastIndexOf("-", StringComparison.Ordinal) + 1);
sid = sid + de.Properties["primaryGroupId"].Value;
//Find the group by its SID
var group = new DirectoryEntry($"LDAP://<SID={sid}>");
group.RefreshCache(new [] {"cn"});
return group.Properties["cn"].Value as string;
}
To use that from your code, you would do this:
var primaryGroupName = GetUserPrimaryGroup((DirectoryEntry) user.GetUnderlyingObject());
That method just returns the name of the group, but you can modify it as you need.
All that said, 513 is always the RID of the built-in Domain Users group.

Related

How to get LDAP nested groups from attribute

I can search the user and find only the groups that the user belongs to. And now i want to return all the groups/roles and assign a user to a specific group/role.
DirectoryEntry and PrincipalContext doesn't work in my case and i have tried that for days.
This is my working code for searching user group/roles which is working fine.
How can i get all the groups/roles?
And Add user to a group/role
Container = “ou=containername,ou=xx,ou=xx,O=xxxxx”
Domain = “mydomain.com”
group = ou=groups,ou=containername,ou=xx,ou=xx,O=xxxx
List<string> roles = new List<string>();
SearchRequest request = new SearchRequest("", "(&(objectClass=person)(mail=myusername))", System.DirectoryServices.Protocols.SearchScope.Subtree);
SearchResponse response = (SearchResponse)con.SendRequest(request);
if (response.Entries.Count == 0)
{
return null;
}
else
{
foreach (SearchResultEntry entry in response.Entries)
{
if (entry.Attributes["member"] != null)
{
roles = (entry.Attributes["member"].GetValues(typeof(string))).ToArray().Select(r => r.ToString()
.Substring(r.ToString().IndexOf("cn=") + 3,
r.ToString().IndexOf(",") - 3))
.ToList();
}
}
}
return roles;
I don't think you're using Active Directory. What tipped me off is that you're getting data from the member attribute of a user. That's not how it works with Active Directory (it would be memberOf).
I'm not entirely sure what you're asking for. Your title mentioned "nested groups", which means when one group is a member of another group. So I assume that would mean that you want to find every group the user is a member of and all groups that those groups are members of, etc. If that's the case, you will really have to find out what type of server you're connecting to before anyone can give you a good answer on that.
But in your question you say "How can i get all the groups/roles?" So does that mean you just want to find every group that exists? To do that, you can just do a new search and use this as the filter:
(objectClass=group)
For adding a user to a group, I think it would be something like this (where userDn is the distinguishedName of the user you want to add, and groupDn is that of the group):
var mod = new DirectoryAttributeModification {
Name = "member",
Operation = DirectoryAttributeOperation.Add
}
mod.Add(userDn);
var response = (ModifyResponse) connectionObject.SendRequest(
new ModifyRequest {
DistinguishedName = groupDn,
Modifications = { mod }
}
);
But I've never actually used LdapConnection, so you might need to tweak it.
By default, the ADLDS or AD MemberOf (User object) Member (Group object) attribute is not retrieved.
Example Solution for User
SearchRequest request = new SearchRequest("", "(&(objectClass=user)(mail=myusername))", System.DirectoryServices.Protocols.SearchScope.Subtree);
request.Attributes.Add("memberOf");
or Group
SearchRequest request = new SearchRequest("", "(&(objectClass=group))", System.DirectoryServices.Protocols.SearchScope.Subtree);
request.Attributes.Add("member");
Default LDAP Filters and Attributes for Users, Groups and Containers

Get AD group membership for the current user on another domain

I have a bit of code to get all of the AD groups for the user currently logged in:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Environment.UserDomainName);
// find a user
UserPrincipal adUser = UserPrincipal.FindByIdentity(ctx, user);
if (adUser == null)
{
Logger.Error("Could not find related Active Directory user");
return;
}
GetUserDetailsFromGroupMembership(adUser.GetGroups().Select(g => g.Name));
This will get all the groups the current user is part of on its registered domain. My username is part of the "EUR" domain, but I also have memberships on the "USA" domain. How do I also query groups in the other domains?
GetGroups() gets all the groups from domains in the same forest, so I have to assume that your two domains are not in the same forest, but are trusted by each other.
I don't know of a way to do this with the AccountManagement namespace, but this is how you would do it with DirectorySearcher.
When accounts get added to groups on an external, trusted domain, they show up as Foreign Security Principal objects, which contain the SID of the account. So that's what you have to search for. You already have the account in the adUser variable, so I'm using adUser.Sid here.
I assume you will always be searching the one other domain, so you can hard code the distinguishedName of the other domain in the first line.
var otherDomainDN = "DC=usa,domain,DC=com";
var ds = new DirectorySearcher(new DirectoryEntry($"LDAP://{otherDomainDN}"),
$"(&(objectClass=group)(member=CN={adUser.Sid},CN=ForeignSecurityPrincipals,{otherDomainDN}))");
ds.PropertiesToLoad.Add("cn");
var otherDomainGroups = new List<string>();
foreach (SearchResult g in ds.FindAll()) {
otherDomainGroups.Add(g.Properties["cn"][0].ToString());
}
After this, otherDomainGroups will be a list of the names of the other groups.

Get all the groups the user belongs to

There are two domains Domain A and Domain B having mutual trust between them (forest level trust).
'DomainA\BiggerGroup' is a user group (Domain local scope) in domain A.
'DomainB\SmallGroup' is a user group (Global scope) in domain B.
DomainA\BigGroup contains DomainB\SmallGroup as a sub-group. And DomainB\SmallGroup contains DomainB\User as a member.
Query:
As an Administrator of DomainB, Can we programmatically list all the groups that DomainB\User belongs to?
WindowsIdentity.Groups is not enumerating DomainA\BiggerGroup. Is there any way we can list all the groups that a user belongs to (including the groups in the trusted domains)?
(WindowsIdentity Class has Group property which "Gets the groups the current Windows user belongs to." - http://msdn.microsoft.com/en-us/library/system.security.principal.windowsidentity(v=vs.110).aspx)
As you are working with two domains having forest level trust between them. I think that you can try again using WindowsIdentity.Groups, but establishing a connection (principal context) with a Global Catalog (GC) Directory in spite of any other DC directory.
Strictly speaking, a user may have different group list when log on to different domain.
This is because domain-local group is local to group's own domain only (as its name suggest).
In your case:
When DomainB\User log on to DomainA, the group list contains DomainA\BiggerGroup and DomainB\SmallGroup
When DomainB\User log on to DomainB, the group list contains DomainB\SmallGroup
In general, the group list of a user will contains:
Global & universal groups from USER domain (DomainB in your case), plus
Domain-local groups from connected domain
(or COMPUTER domain if you are logging on a computer)
Other well-known groups, like "Authenticated Users", "Everyone"
(can ignore this if you are only interested in AD groups)
So, w.r.t which domain you want to find out the group list of DomainB\User?
Solution:
To get an accurate group list of a user (without providing password of that user), you can make use of the S4U Kerberos Extensions. (See the S4U2Self section in link below)
http://msdn.microsoft.com/en-us/magazine/cc188757.aspx
The link suggest to use WindowsIdentity. But the WindowsIdentity solution has one problem.
// parameter must be in upn format
WindowsIdentity identity = new WindowsIdentity("User#DomainB.com");
The problem is you cannot control from which domain to get the domain-local groups.
e.g. On computer joined to DomainA, log on as user in DomainB, get WindowsIdentity for user in DomainC. It will get domain-local groups from Domain A, B or C?
Or you may use the LsaLogonUser Win32 function as mentioned in the link. But it takes 14 parameters...
I never tried that before, can't comment on this.
You need to get the token groups for the user. It will return all effective gorup memberships both direct and indirect due to nesting including other domains.
// this method will return all groups where the the user is a direct and indirect member of
public static bool getTokenGroups(string domainFQDN, string alias, ref List<string> userGroups)
{
bool result = false;
try
{
SearchResult sr = default(SearchResult);
using (DirectoryEntry domainDE = new DirectoryEntry("LDAP://" + domainFQDN, "domain\\cn", "password", AuthenticationTypes.Secure))
{
using (DirectorySearcher searcher = new DirectorySearcher(domainDE))
{
searcher.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", alias);
sr = searcher.FindOne();
if (sr != null)
{
using (DirectoryEntry user = sr.GetDirectoryEntry())
{
user.RefreshCache(new string[] { "tokenGroups" });
for (int i = 0; i < user.Properties["tokenGroups"].Count; i++)
{
SecurityIdentifier sid = new SecurityIdentifier((byte[])user.Properties["tokenGroups"][i], 0);
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
//do something with the SID or name (nt.Value)
if(nt.Value.IndexOf('\\') > -1)
userGroups.Add(nt.Value.Split('\\')[1]);
else
userGroups.Add(nt.Value);
}
}
}
}
}
}
catch (Exception ex)
{
EventLog.WriteEntry("source name", MethodBase.GetCurrentMethod().DeclaringType + "." + MethodBase.GetCurrentMethod().Name + "\r\n\r\nUnable to get user's token groups for domain: " + domainFQDN + " user: " + alias + "\r\n\r\n" + ex.Message, EventLogEntryType.Error);
}
return result;
}

Get all the parent AD groups a user belongs to

I want to get all the Active Directory groups in which a particular user is a member.
I do have the script to get all the immediate AD groups for a user (see codes below).
However, how do I get all the parent groups for each of the immediate AD groups the user belongs to?
e.g. I am directly part of the AD group called IT Team Managers. This group is member of a parent group called IT Team National etc. How do I get this parent group from my code?
Thanks so much in advance!
DirectorySearcher ouSearch = new DirectorySearcher(entry);
ouSearch.Filter = "(&(objectClass=User)(sAMAccountName=" + username + "))";
//ouSearch.PropertiesToLoad.Add("samAccountName");
ouSearch.PropertiesToLoad.Add("memberOf");
ouSearch.SearchScope = SearchScope.Subtree;
SearchResult allOUS = ouSearch.FindOne();
//foreach (string g in allOUS.Properties["memberOf"])
{
equalsIndex = g.IndexOf("=", 1);
commaIndex = g.IndexOf(",", 1);
if (equalsIndex == -1)
{
return null;
}
groupNames.Append(g.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));
groupNames.Append(",");
}
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// get the "authorization groups" the user is a member of - recursively
var authGroups = user.GetAuthorizationGroups();
// iterate over groups
foreach(Principal p in authGroups)
{
// do something with groups ....
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!

How to get SID of a group once i get groups of a user in Active Directory?

I am using DirectorySearcher to get groups of a User in ActiveDirectory.
My Question is how to get SID associated with each group once i get user groups using "memberOf"?
I am working in .NETFramework 2.0 Environment.
DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", sUserDomain));
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = string.Format("(&(objectClass=user) (cn= {0}))", ui.DisplayName.ToString());
mySearcher.PropertiesToLoad.Add("memberOf");
SearchResult searchresult = mySearcher.FindOne();
There is no way to do it in one single LDAP search because memberOf returns a distinguish name. You have to do another bind to get the objectSid attribute from the group object. Here is the code.
DirectoryEntry entry = new DirectoryEntry(string.Format("LDAP://{0}", sUserDomain));
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = string.Format("(&(objectClass=user) (cn= {0}))", ui.DisplayName.ToString());
mySearcher.PropertiesToLoad.Add("memberOf");
SearchResult searchresult = mySearcher.FindOne();
foreach (string dn in searchresult.Properties["memberOf"])
{
DirectoryEntry group = new DirectoryEntry(string.Format("LDAP://{0}/{1}", sUserDomain, dn));
SecurityIdentifier sid = new SecurityIdentifier(group.Properties["objectSid"][0] as byte[], 0);
Console.Out.WriteLine(sid.Value);
}
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// define context for current domain
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "YourNameHere");
if (user != null)
{
// get groups the user is a member of
var groups = current.GetGroups();
// iterate over all those groups
foreach(var group in groups)
{
// fetch the SID for each group
var sid = group.Sid;
}
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Have a look at his article:
Retrieving user SID using DirectoryEntry and DirectorySearcher
This gives you a full working example for retrieving the SID.

Categories

Resources