Como obter todos os grupos do AD para um usuário específico?

Eu verifiquei este post já. Mas isso não responde à minha pergunta. Eu quero obter todos os grupos de diretórios ativos em que um usuário específico é um membro.

Eu escrevi o seguinte código. Mas eu não sou capaz de prosseguir, pois não sei como fornecer o filtro e como acessar as propriedades.

class Program { static void Main(string[] args) { DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com"); DirectorySearcher searcher = new DirectorySearcher(de); searcher.Filter = "(&(ObjectClass=group))"; searcher.PropertiesToLoad.Add("distinguishedName"); searcher.PropertiesToLoad.Add("sAMAccountName"); searcher.PropertiesToLoad.Add("name"); searcher.PropertiesToLoad.Add("objectSid"); SearchResultCollection results = searcher.FindAll(); int i = 1; foreach (SearchResult res in results) { Console.WriteLine("Result" + Convert.ToString(i++)); DisplayProperties("distinguishedName", res); DisplayProperties("sAMAccouontName", res); DisplayProperties("name", res); DisplayProperties("objectSid", res); Console.WriteLine(); } Console.ReadKey(); } private static void DisplayProperties(string property, SearchResult res) { Console.WriteLine("\t" + property); ResultPropertyValueCollection col = res.Properties[property]; foreach (object o in col) { Console.WriteLine("\t\t" + o.ToString()); } } } 

Alguma ideia?

Apenas consulte a propriedade “memberOf” e repita o retorno, por exemplo:

  search.PropertiesToLoad.Add("memberOf"); StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited SearchResult result = search.FindOne(); int propertyCount = result.Properties["memberOf"].Count; String dn; int equalsIndex, commaIndex; for (int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++) { dn = (String)result.Properties["memberOf"][propertyCounter]; equalsIndex = dn.IndexOf("=", 1); commaIndex = dn.IndexOf(",", 1); if (-1 == equalsIndex) { return null; } groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1)); groupNames.Append("|"); } return groupNames.ToString(); 

Isso apenas coloca os nomes dos grupos na string groupNames, delimitada por pipe, mas quando você gira, pode fazer o que quiser com eles.

Você deve usar System.DirectoryServices.AccountManagement . É muito mais fácil. Aqui está um artigo de projeto de código legal que oferece uma visão geral sobre todas as classs nesta DLL.

Como você apontou, sua abordagem atual não descobre o grupo primário. Na verdade, é muito pior do que você pensou. Há mais alguns casos em que isso não funciona, como o grupo local de domínio de outro domínio. Você pode verificar aqui para mais detalhes. Aqui está como o código se parece se você mudar para usar System.DirectoryServices.AccountManagement. O código a seguir pode localizar os grupos imediatos aos quais esse usuário está designado, o que inclui o grupo principal.

 UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username"); foreach (GroupPrincipal group in user.GetGroups()) { Console.Out.WriteLine(group); } 

Use tokenGroups :

 DirectorySearcher ds = new DirectorySearcher(); ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username); SearchResult sr = ds.FindOne(); 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) } 

Nota: isso só recebe grupos de segurança

O exemplo a seguir é do artigo do Projecto de Código, (Quase) Tudo no Active Directory via C # :

 // userDn is a Distinguished Name such as: // "LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com" public ArrayList Groups(string userDn, bool recursive) { ArrayList groupMemberships = new ArrayList(); return AttributeValuesMultiString("memberOf", userDn, groupMemberships, recursive); } public ArrayList AttributeValuesMultiString(string attributeName, string objectDn, ArrayList valuesCollection, bool recursive) { DirectoryEntry ent = new DirectoryEntry(objectDn); PropertyValueCollection ValueCollection = ent.Properties[attributeName]; IEnumerator en = ValueCollection.GetEnumerator(); while (en.MoveNext()) { if (en.Current != null) { if (!valuesCollection.Contains(en.Current.ToString())) { valuesCollection.Add(en.Current.ToString()); if (recursive) { AttributeValuesMultiString(attributeName, "LDAP://" + en.Current.ToString(), valuesCollection, true); } } } } ent.Close(); ent.Dispose(); return valuesCollection; } 

Basta chamar o método Groups com o nome distinto para o usuário e passar o sinalizador bool para indicar se você deseja include associações de grupos filho / aninhadas em sua ArrayList resultante:

 ArrayList groups = Groups("LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com", true); foreach (string groupName in groups) { Console.WriteLine(groupName); } 

Se você precisar fazer algum nível sério de programação do Active Directory no .NET, eu recomendo fortemente marcar e revisar o artigo do Projecto de Código que mencionei acima.

Este código funciona ainda mais rápido (dois 1,5 mais rápido que minha versão anterior):

  public List GetUserGroups(WindowsIdentity identity) { List groups = new List(); String userName = identity.Name; int pos = userName.IndexOf(@"\"); if (pos > 0) userName = userName.Substring(pos + 1); PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com"); UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com"); DirectorySearcher search = new DirectorySearcher(de); search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))"; search.PropertiesToLoad.Add("cn"); search.PropertiesToLoad.Add("samaccountname"); search.PropertiesToLoad.Add("memberOf"); SearchResultCollection results = search.FindAll(); foreach (SearchResult sr in results) { GetUserGroupsRecursive(groups, sr, de); } return groups; } public void GetUserGroupsRecursive(List groups, SearchResult sr, DirectoryEntry de) { if (sr == null) return; String group = (String)sr.Properties["cn"][0]; if (String.IsNullOrEmpty(group)) { group = (String)sr.Properties["samaccountname"][0]; } if (!groups.Contains(group)) { groups.Add(group); } DirectorySearcher search; SearchResult sr1; String name; int equalsIndex, commaIndex; foreach (String dn in sr.Properties["memberof"]) { equalsIndex = dn.IndexOf("=", 1); if (equalsIndex > 0) { commaIndex = dn.IndexOf(",", equalsIndex + 1); name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1); search = new DirectorySearcher(de); search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))"; search.PropertiesToLoad.Add("cn"); search.PropertiesToLoad.Add("samaccountname"); search.PropertiesToLoad.Add("memberOf"); sr1 = search.FindOne(); GetUserGroupsRecursive(groups, sr1, de); } } } 

Aqui está o código que funcionou para mim:

 public ArrayList GetBBGroups(WindowsIdentity identity) { ArrayList groups = new ArrayList(); try { String userName = identity.Name; int pos = userName.IndexOf(@"\"); if (pos > 0) userName = userName.Substring(pos + 1); PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com"); UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com"); DirectorySearcher search = new DirectorySearcher(de); search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))"; search.PropertiesToLoad.Add("samaccountname"); search.PropertiesToLoad.Add("cn"); String name; SearchResultCollection results = search.FindAll(); foreach (SearchResult result in results) { name = (String)result.Properties["samaccountname"][0]; if (String.IsNullOrEmpty(name)) { name = (String)result.Properties["cn"][0]; } GetGroupsRecursive(groups, de, name); } } catch { // return an empty list... } return groups; } public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn) { DirectorySearcher search = new DirectorySearcher(de); search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))"; search.PropertiesToLoad.Add("memberof"); String group, name; SearchResult result = search.FindOne(); if (result == null) return; group = @"RIOMC\" + dn; if (!groups.Contains(group)) { groups.Add(group); } if (result.Properties["memberof"].Count == 0) return; int equalsIndex, commaIndex; foreach (String dn1 in result.Properties["memberof"]) { equalsIndex = dn1.IndexOf("=", 1); if (equalsIndex > 0) { commaIndex = dn1.IndexOf(",", equalsIndex + 1); name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1); GetGroupsRecursive(groups, de, name); } } } 

Eu medi seu desempenho em um loop de 200 execuções contra o código que usa o método recursivo AttributeValuesMultiString ; e funcionou 1,3 vezes mais rápido. Pode ser por causa de nossas configurações do AD. Ambos os snippets deram o mesmo resultado.

 PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, "DomainName", UserAccountOU, UserName, Password); UserPrincipal UserPrincipalID = UserPrincipal.FindByIdentity(pc1, IdentityType.SamAccountName, UserID); searcher.Filter = "(&(ObjectClass=group)(member = " + UserPrincipalID.DistinguishedName + ")); 

Se você tiver uma conexão LDAP com um nome de usuário e senha para se conectar ao Active Directory, aqui está o código que usei para se conectar corretamente:

 using System.DirectoryServices.AccountManagement; // ... // Connection information var connectionString = "LDAP://domain.com/DC=domain,DC=com"; var connectionUsername = "your_ad_username"; var connectionPassword = "your_ad_password"; // Get groups for this user var username = "myusername"; // Split the LDAP Uri var uri = new Uri(connectionString); var host = uri.Host; var container = uri.Segments.Count() >=1 ? uri.Segments[1] : ""; // Create context to connect to AD var princContext = new PrincipalContext(ContextType.Domain, host, container, connectionUsername, connectionPassword); // Get User UserPrincipal user = UserPrincipal.FindByIdentity(princContext, IdentityType.SamAccountName, username); // Browse user's groups foreach (GroupPrincipal group in user.GetGroups()) { Console.Out.WriteLine(group.Name); }