Проходим сквозь дебри вложенных групп

Вложенные группы являются мощным инструментом, однако они добавляют дополнительный уровень сложности. Поскольку некоторые члены группы сами могут быть группами, вы часто не можете простыми средствами просмотреть членов группы и определить, какие пользователи подвергнутся воздействию прав, назначенных для доступа к ресурсам. Оснастка Active Directory Users and Computers консоли Microsoft Management Console (MMC) не сможет вам помочь, поскольку она отображает пользователей, которые являются прямыми членами группы. Вы можете только двойным нажатием на записи о каждом члене группы посмотреть их индивидуальное членство. Упростить решение этой задачи возможно при помощи простого сценария, который выполнят за вас эту работу и предоставит сведения о членстве во вложенных группах.

Атрибуты группы

Чтобы проиллюстрировать вложенность групп, допустим, что структура групп подобна иерархии факультета в университете. Каждый из классов отделения - это группа, состоящая из студентов этого класса. Все вместе группы классов составляют группу факультета. Наконец, группа факультета является членом группы курса университета. Подобная структура вложенных групп обеспечивает гибкость в присвоении прав доступа к ресурсам, таким как общие аудитории для всего курса и к специальным аудиториям, доступным только для студентов определенных классов.

Групповые объекты в AD имеют два атрибута, имеющие отношение к членству в группах. Один из таких атрибутов сохраняет ссылки на объекты, которые являются прямыми членами группы. Другой атрибут memberOf хранит ссылки на другие группы,. к которым принадлежит данная группа.

Эти два атрибута являются ссылками на соответствующие атрибуты AD. У вас нет возможности изменить атрибут memberOf для изменения членства в группе. Вы сможете модифицировать только атрибут member принадлежности к группе. AD автоматически рассчитывает атрибут memberOf для группы или другого типа объекта такого, как USER. На основе принадлежности и прямого членства объекта во всех своих группах. Атрибут memberOf позволяет вам идентифицировать группы,. членом которых является объект, без дополнительного поиска по всем группам. Например, если учетная запись с ID STUDENT 1 является членом группы Engineering, атрибут объекта memberOf содержит ссылку (обычно полное имя DN) на группу Engineering. Атрибут member группы Engineering в свою очередь содержит ссылку, (снова DN), указывающую на Student 1.

Использование сценария

Теперь, когда вы имеете атрибуты групп, воспользуемся сценарием, перечисляющим членов вложенных групп. На Листинге 1 представлен код сценария enum_groups.vbs, который выводит членов вложенных групп в виде иерархического списка. Чтобы получить такой вид списка вам необходимо воспользоваться сервером сценариев CScript Windows Script Host (WSH) для запуска enum_groups.vbs.

Для использования сценария скопируйте его в локальную папку на вашем компьютере и выполните следующую команду:

C:Scripts>cscript enum_groups.vbs

Эта команда заставляет сценарий выводить членов группы Domain Admins домена, в котором вы прошли авторизацию. Другой способ запуска сценария - это ввести DN - имя группы, членов которой вы хотите получить. Например, команда

C:Scripts> cscript enum_groups.vbs
cn=engineering,cn=users,dc=foo,dc=edu

определяет членов группы Engineering school и осуществляет вывод списка на экран, показанный на Рисунке 1.

Рисунок 1 Вывод результатов сценарием enum_groups.vbs

Получение информации о группах из AD

В сценарии enum_groups.vbs, код фрагмента A, представленный на Листинге 1 показывает определение того, список членов какой группы необходимо получить. Чтобы узнать, вводил ли пользователь какие-либо параметры при запуске сценария, сценарий создает объект WScript.Arguments, который обеспечивает доступ ко всем аргументам командной строки. Если число аргументов, введенных при запуске сценария не равно 1, сценарий считает, что пользователь не задал каких-либо аргументов, либо задал их слишком много.

В этом случае сценарий выполняет независимую от сервера привязку к объекту RootDSE. Процесс DCLocator автоматически привязывается к контроллеру домена (DC) того домена, в котором пользователь запускает сценарий. Затем сценарий конструирует (воссоздает) полное имя DN группы Domain Admins так, что если кто-либо запускает сценарий без определения того, список членов каких групп необходимо получить, сценарий будет делать хоть какую-то операцию. Когда пользователь вводит хотя бы один параметр, сценарий помещает этот параметр в переменную strGroupDN.

Код фрагмента B начинает создавать объект dictionary VBScript с именем dicSeenGroupMember. Сценарий использует объект dictionary для отслеживания групп, которые уже показаны с целью предотвращения зацикливания в отображении вложенных групп. Круговое вложение групп случается в тех случаях, когда вы имеете цикл в цепочке членства в группах. Например, если группа groupA является членом группы groupB, а группа groupB является членом группы groupC, а groupC является,в свою очередь, членом группы groupA. В описанном случае мы имеем цикл в цепочке членства в группах. Круговое вложение групп не обязательно является вредным явлением. Просто это необходимо иметь в виду при работе с вложенными группами.

Наконец код фрагмента B вызывает функцию DisplayMembers. DisplayMembers требует для своей работы три параметра: ADsPath - путь AD к группе, членство в которой необходимо просмотреть. Второй параметр - это количество пробелов при выводе членов группы на экран. Третий параметр - это ссылка на объект dictionary. Далее я в деталях объясню назначение этих параметров.

Функция DisplayMembers

Фрагмент C сценария содержит основной полезный код функции DisplayMembers. DisplayMembers представляет из себя рекурсивную функцию, перечисляющую членов группы. Затем функция вызывает сама себя для поиска тех членов группы, которые сами являются группами. После этого DisplayMembers начинает выполнение цикла For Each...Next. Цикл производит итерацию по всем членам группы, путём вызова метода Members для каждого члена. Только объекты IADsGroup имеют метод Members. Если ADsPath не принадлежит к объектам типа группа, сценарий остановит свою работу на первой строке цикла. Если ADsPath содержит путь к группе, сценарий возвратит для каждого члена группы объект IADsMember

Внутри цикла enum_groups.vbs сначала использует метод Get для восстановления полного имени DN объекта и затем выводит его на экран. В этой точке сценария второй параметр DisplayMembers, задающий число пробелов, вступает в игру. Поскольку я хочу, чтобы сценарий показывал иерархию вложенных групп, сценарий выводит пробелы перед тем, как вывести имя члена группы. Позже, когда DisplayMembers вызывает сам себя, он увеличивает число пробелов для выделения каждого уровня в иерархии членов.

Затем enum_groups.vbs, используя метод Class, проверяет тип класса объекта member. Для групп метод Class возвращает значение group.Это означает, что обнаружены вложенные группы и что сценарию придется просматривать и выводить информацию о членах вложенных групп. Однако перед тем как enum_groups.vbs начнет перечисление членов вложенной группы, сценарию необходимо убедиться в том, что он еще не считывал эту группу. Я не хочу, чтобы сценарий считывал информацию о группе более одного раза, чтобы избежать появления бесконечных циклов по причинам, описанным ранее. Для определения того, считана ли уже информация по группе, enum_groups.vbs проверяет, присутствует ли имя этой группы в объекте - словаре dictionary. Если там эта группа отсутствует, сценарий начинает перечисление ее объектов.

Во время перечисления состава группы, enum_groups.vbs первым делом добавляет имя этой группы в объект dictionary для того, чтобы сценарий не просматривал эту группу снова. Затем сценарий опять вызывает DisplayMembers, и в игру вступает рекурсивный алгоритм. Помните, что первым параметром DisplayMembers является ADsPath - путь AD к перечисляемой группе. Этот параметр сценарий получает, используя метод AdsPath. Вторым параметром DisplayMembers является число пробелов, использующихся для выделения вложенных групп при отображении. Сценарий добавляет два пробела к идентификатору вложенной группы. Последний из параметров - это копия объекта Dictionary.

Это всё, что я хотел сказать о enum_groups.vbs. Рекурсия в этом сценарии работает по той причине, что сценарием отслеживаются группы, которые уже перечислены и вызывает DisplayMembers только для групп, которые еще не перечислялись. При помощи рекурсивной функции вам всегда необходимо иметь критерий выхода из операции. В сценарии enum_groups.vbs в качестве такого критерия выступает объект dictionary.

Следите за статьями об управлении группами

Другая задача, которая встанет перед вами при использовании вложенных групп - это просмотр полного членства пользователя в группах, включая его членство во вложенных группах. Вы можете решить эту задачу, используя атрибут пользователя memberOf. Он позволяет увидеть все группы, к которым принадлежит пользователь, пройдя всю иерархию членства в каждой из этих групп. В будущей статье я опишу, как можно при помощи короткого сценария выполнить эту задачу.


Листинг 1. Enum_groups.vbs
option explicit
' НАЧАЛО ФРАГМЕНТА A
Dim objArgs, strGroupDN
set objArgs = WScript.Arguments
if objArgs.Count <> 1 then
 Dim objRootDSE
 set objRootDSE = GetObject("LDAP://RootDSE")
 strGroupDN = "cn=Domain Admins,cn=users," &
 objRootDSE.Get("defaultNamingContext")
else
 strGroupDN = objArgs.Item(0)
end if
' КОНЕЦ ФРАГМЕНТА A
' НАЧАЛО ФРАГМЕНТА B
Dim dicSeenGroupMember
set dicSeenGroupMember = CreateObject("Scripting.Dictionary")
Wscript.Echo "Members of " & strGroupDN & ":"
DisplayMembers "LDAP://" & strGroupDN,
 " ", dicSeenGroupMember
' КОНЕЦ ФРАГМЕНТА B
' НАЧАЛО ФРАГМЕНТА C
Function DisplayMembers (strGroupADsPath, strSpaces,
 dicSeenGroupMember)
 Dim objGroup, objMember
 set objGroup = GetObject(strGroupADsPath)
 for each objMember In objGroup.Members
  Wscript.Echo strSpaces & objMember.Get("distinguishedname")
  if objMember.Class = "group" then
   if dicSeenGroupMember.Exists(objMember.ADsPath) then
   Wscript.Echo strSpaces & " ^ already seen
 group member " & _
          "(stopping to avoid loop)"
   else
   dicSeenGroupMember.Add objMember.ADsPath, 1
   DisplayMembers objMember.ADsPath, strSpaces & " ", _
       dicSeenGroupMember
   end if
  end if
 next
End Function
' КОНЕЦ ФРАГМЕНТА C