Как получить группы пользователей в Active Directory? (С#, asp.net)
Я использую этот код, чтобы получить группы текущего пользователя. Но я хочу вручную дать пользователю, а затем получить его группы. Как я могу это сделать?
using System.Security.Principal;
public ArrayList Groups()
{
ArrayList groups = new ArrayList();
foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
return groups;
}
Ответы
Ответ 1
Если вы используете .NET 3.5 или выше, вы можете использовать новое пространство имен System.DirectoryServices.AccountManagement
(S.DS.AM), которое делает это намного проще, чем раньше.
Подробнее об этом читайте здесь: Руководство для руководителей безопасности в .NET Framework 3.5
Обновление: старые статьи журнала MSDN больше не в сети, вам, к сожалению, нужно загрузить CHM за январь 2008 MSDN magazine от Microsoft и прочитайте статью там.
В принципе, вам нужно иметь "главный контекст" (обычно ваш домен), пользовательский принцип, а затем вы легко получаете его группы:
public List<GroupPrincipal> GetGroups(string userName)
{
List<GroupPrincipal> result = new List<GroupPrincipal>();
// establish domain context
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
// find your user
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);
// if found - grab its groups
if(user != null)
{
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
// iterate over all groups
foreach(Principal p in groups)
{
// make sure to add only group principals
if(p is GroupPrincipal)
{
result.Add((GroupPrincipal)p);
}
}
}
return result;
}
и что все есть! Теперь у вас есть результат (список) групп авторизации, к которым принадлежит пользователь, - перебирайте их, распечатывайте их имена или все, что вам нужно сделать.
Обновление. Чтобы получить доступ к определенным свойствам, которые не отображаются на объекте UserPrincipal
, вам необходимо выполнить поиск в базовом DirectoryEntry
:
public string GetDepartment(Principal principal)
{
string result = string.Empty;
DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);
if (de != null)
{
if (de.Properties.Contains("department"))
{
result = de.Properties["department"][0].ToString();
}
}
return result;
}
Обновление № 2: кажется, не должно быть слишком сложно скомпоновать эти два фрагмента кода... но нормально - вот оно:
public string GetDepartment(string username)
{
string result = string.Empty;
// if you do repeated domain access, you might want to do this *once* outside this method,
// and pass it in as a second parameter!
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
// find the user
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);
// if user is found
if(user != null)
{
// get DirectoryEntry underlying it
DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);
if (de != null)
{
if (de.Properties.Contains("department"))
{
result = de.Properties["department"][0].ToString();
}
}
}
return result;
}
Ответ 2
GetAuthorizationGroups()
не находит вложенные группы. Чтобы действительно получить все группы, данный пользователь является членом (включая вложенные группы), попробуйте следующее:
using System.Security.Principal
private List<string> GetGroups(string userName)
{
List<string> result = new List<string>();
WindowsIdentity wi = new WindowsIdentity(userName);
foreach (IdentityReference group in wi.Groups)
{
try
{
result.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (Exception ex) { }
}
result.Sort();
return result;
}
Я использую try/catch
, потому что у меня были некоторые исключения из 2 из 200 групп в очень большом AD, потому что некоторые идентификаторы SID больше не были доступны. (Вызов Translate()
выполняет преобразование SID → Name.)
Ответ 3
Прежде всего, GetAuthorizationGroups() - отличная функция, но, к сожалению, имеет 2 недостатка:
- Производительность плохой, особенно в большой компании со многими пользователями и группами. Он извлекает намного больше данных, чем вам действительно нужно, и вызывает вызов сервера для каждой итерации цикла в результате.
- Он содержит ошибки, которые могут привести к тому, что ваше приложение перестанет работать "когда-нибудь", когда группы и пользователи будут развиваться. Microsoft признала проблему и связана с некоторыми SID. Ошибка, которую вы получите, - "Произошла ошибка при перечислении групп"
Поэтому я написал небольшую функцию для замены GetAuthorizationGroups() с лучшей производительностью и безопасностью. Он выполняет только 1 вызов LDAP с запросом с использованием индексированных полей. Он может быть легко расширен, если вам нужно больше свойств, чем только имена групп (свойство "cn" ).
// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain")
public static List<string> GetAdGroupsForUser2(string userName, string domainName = null)
{
var result = new List<string>();
if (userName.Contains('\\') || userName.Contains('/'))
{
domainName = userName.Split(new char[] { '\\', '/' })[0];
userName = userName.Split(new char[] { '\\', '/' })[1];
}
using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName))
using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName))
using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name)))
{
searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName);
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("cn");
foreach (SearchResult entry in searcher.FindAll())
if (entry.Properties.Contains("cn"))
result.Add(entry.Properties["cn"][0].ToString());
}
return result;
}
Ответ 4
В пределах AD каждый пользователь имеет свойство memberOf
. Он содержит список всех групп, к которым принадлежит.
Вот пример небольшого кода:
// (replace "part_of_user_name" with some partial user name existing in your AD)
var userNameContains = "part_of_user_name";
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
// Apply some filter to focus on only some specfic objects
searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains);
return searcher;
});
var directoryEntriesFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var memberOf = directoryEntriesFound.Select(entry =>
{
using (entry)
{
return new
{
Name = entry.Name,
GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString())
};
}
});
foreach (var item in memberOf)
{
Debug.Print("Name = " + item.Name);
Debug.Print("Member of:");
foreach (var groupName in item.GroupName)
{
Debug.Print(" " + groupName);
}
Debug.Print(String.Empty);
}
}
Ответ 5
В моем случае единственным способом, который я мог бы использовать GetGroups() без каких-либо эксплойтов, было добавление пользователя (USER_WITH_PERMISSION) в группу, у которой есть разрешение на чтение AD (Active Directory). Крайне важно создать PrincipalContext, передав этого пользователя и пароль.
var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS");
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
var groups = user.GetGroups();
Шаги, которые вы можете выполнить в Active Directory, чтобы заставить его работать:
- В Active Directory создайте группу (или возьмите ее), а на вкладке secutiry добавьте "Группа доступа к авторизации Windows"
- Нажмите кнопку "Дополнительно"
- Выберите "Группа доступа к авторизации Windows" и нажмите "Просмотр"
- Проверьте "Чтение токеновGroupsGlobalAndUniversal"
- Найдите нужного пользователя и добавьте в группу, созданную (взятую) с первого шага.