Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 3. Реализуем необходимые операции::Журнал СА 5.2009
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 1156
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 1124
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 3660
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 3905
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 3086
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 3. Реализуем необходимые операции

Архив номеров / 2009 / Выпуск №5 (78) / Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 3. Реализуем необходимые операции

Рубрика: Администрирование /  Продукты и решения

Вадим Андросов

Делегируем права на перемещение
учетных записей пользователей в Active Directory
Часть 3. Реализуем необходимые операции

Продолжим изучать функции поддержки работы надстройки для Windows 2003 Server, реализующей перевод пользователя из одного подразделения в другое силами двух администраторов. В частности, будут затронуты вопросы программной манипуляции объектами Active Directory, работы с событиями с помощью WMI, модификации списков контроля доступа (ACL).

Делегирование полномочий менеджеру

Реализуем механизм автоматического делегирования прав на манипуляцию объектами пользователей. Конечно, эта операция может быть выполнена и вручную. Однако было бы гораздо удобнее назначить руководителя организационной единицы. Необходимые права должны быть предоставлены менеджеру автоматически. Соответственно при потере пользователем роли менеджера подразделения эти права нужно также автоматически отнять.

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

Методы, связанные с делегированием, будут также реализованы в рамках единого класса надстройки UserMove.Engine.

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

function delegateOU(ouPath, userPath)

dim trustee, sec, acl, ace, ou, user, i

Set ou = getObject(ouPath)

Set user = getObject(userPath)

Для начала нужно привязаться к объекту «Список контроля доступа» текущей организационной единицы, как это делалось в методах проверки, реализованных в предыдущей части статьи [2].

Set sec = ou.Get("ntSecurityDescriptor")

Set acl = sec.DiscretionaryAcl

trustee = info.domainShortName & "\" & user.samAccountName

for i = 0 to UBound(delegationClasses)

Затем по очереди создаются необходимые записи списка контроля доступа для каждого из классов массива delegationClasses. Они уже обсуждались при реализации проверки, поэтому подробно останавливаться на них не будем. Как будет видно из программы ниже, для этого нужно создать объект типа AccessControlEntry, проинициализировать необходимые поля и записать новый элемент в список контроля доступа с помощью метода списка AddAce.

Set ace = createAcceptAce(ADS_RIGHT_DS_CREATE_CHILD Or ADS_RIGHT_DS_DELETE_CHILD, delegationClasses(i), ADS_FLAG_OBJECT_TYPE_PRESENT, Trustee)

Эта запись разрешает создавать в организационной единице объекты заданного типа.

acl.AddAce ace

Set ace = createAcceptAce(FULL_CONTROL , delegationClasses(i), ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, Trustee)

Эта запись разрешает изменение свойств объектов, принадлежащих классу с идентификатором delegation Classes(i)

acl.AddAce ace

next

Обновленный список привязывается к отделу. Важно не забыть вызвать метод организационной единицы SetInfo, чтобы изменения были физически сохранены.

sec.DiscretionaryAcl = acl

ou.Put "ntSecurityDescriptor", Array(sec)

ou.SetInfo

Кроме того, в первой части статьи [1] упоминалась специальная группа безопасности, содержащая всех менеджеров по персоналу. Вызов следующей подпрограммы будет добавлять текущего пользователя в такую группу. Его реализация будет рассмотрена позже.

makeManager user

end function

Рассмотрим функцию создания записи списка контроля доступа.

function createAcceptAce(accessMask, classGUID, flag, trustee)

Сначала создается экземпляр класса AccessControlEntry, посредством которого будет предоставляться заданное право.

set createAcceptAce = CreateObject("AccessControlEntry")

createAcceptAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT

createAcceptAce.accessMask = accessMask

Затем в зависимости от параметра flag целевой объект записывается в поле ObjectType или InheritedObjectType.

select case flag

case ADS_FLAG_OBJECT_TYPE_PRESENT:

createAcceptAce.ObjectType = classGUID

createAcceptAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE

case ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT:

createAcceptAce.InheritedObjectType = classGUID

createAcceptAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE + ADS_ACEFLAG_INHERIT_ONLY_ACE

end select

createAcceptAce.Flags = flag

createAcceptAce.Trustee = trustee

end function

Потребуется и обратная функция для отмены делегирования. Сразу нужно оговориться, что фактически будет происходить удаление тех записей списка контроля доступа, которые добавлялись в функции делегирования выше. Если пользователь имеет те же права, например, участвуя в определенных группах, то их он не потеряет. То есть речь идет не об отмене делегирования управления организационной единицей как таковой, а, скорее, о функции, обратной delegateOU. Такое поведение было выбрано, чтобы минимизировать влияние надстройки на другие политики безопасности предприятия.

Листинг 1. Отмена делегирования управления организационной единицей

function undelegateOU(ouPath, userPath)

dim ou, user, i

set ou = getObject(ouPath)

set user = getObject(userPath)

for i = 0 to UBound(delegationClasses)

undelegate ou, user, FULL_CONTROL , delegationClasses(i), true

undelegate ou, user, ADS_RIGHT_DS_DELETE_CHILD Or ADS_RIGHT_DS_CREATE_CHILD, delegationClasses(i), false

next

makeNotManager user

end function

Отмена конкретного разрешения выполняется следующей подпрограммой. Все записи списка контроля доступа проверяются с помощью метода shouldBeDeleted и, если он вернул истину, удаляются.

Листинг 2. Удаление из ACL записей, позволяющих управлять организационной единицей

function undelegate(ou, user, oper, targetClass, isInherited)

Dim sec, acl, ace

Set sec = ou.Get("ntSecurityDescriptor")

Set acl = sec.DiscretionaryAcl

For Each ace In acl

if shouldBeDeleted(user, ace, oper, targetClass, isInherited) then

acl.RemoveAce ace

end if

Next

sec.DiscretionaryAcl = acl

ou.Put "ntSecurityDescriptor", Array(sec)

ou.SetInfo

end function

Следующая подпрограмма выясняет, нужно ли удалить определенную запись списка контроля доступа при удалении у пользователя прав менеджера по персоналу. Она проверяет, что запись относится к целевому пользователю и предоставляет права на управление организационной единицей.

Листинг 3. Требуется ли удалять запись ACL для отмены делегирования

function shouldBeDeleted(user, ace, oper, targetClass, isInherited)

shouldBeDeleted = false

if ace.trustee <> (info.domainShortName & "\" & user.samAccountName) then exit function

dim classGUID

if isInherited then

classGUID = ace.InheritedObjectType

else

classGUID = ace.ObjectType

end if

if (classGUID = targetClass) and isMask(ace.accessMask, oper) then

if ace.AceType = _

ADS_ACETYPE_ACCESS_ALLOWED_OBJECT then

shouldBeDeleted = true

exit function

end if

end if

end function

Наконец, рассмотрим подпрограммы поддержки актуальности группы безопасности менеджеров по персоналу. Было принято решение назвать ее Staff Managers. Жестко прописанное название специальной группы не является самым удачным решением. Я остановился на нем, чтобы не отвлекать внимания от сущности надстройки мелкими деталями реализации. Вся функциональность описывается с помощью двух методов: makeManager (добавляет пользователя в группу менеджеров при назначении его руководителем организационной единицы) и makeNotManager (соответственно удаляет пользователя из группы. Для привязки к группе я использовал провайдер WinNT, чтобы не зависеть от расположения объекта группы в иерархии.

Итак, первый метод добавления пользователя в группу. В нем сначала происходит привязка к объекту группы с помощью провайдера WinNT. Для этого используется путь, состоящий только из домена и имени группы, например WinNT://MARKLAR/Staff Managers. Сама группа может быть расположена в любом контейнере. Затем составляется описатель пользователя в таком же формате (переменная userWinNTPath). Если пользователь уже является членом группы, функция завершает работу. В противном случае пользователь добавляется в группу с помощью метода add.

Листинг 4. Назначение менеджера по персоналу для подразделения

function makeManager(newManager)

dim managerGroup, userWinNTPath

set managerGroup = getObject("WinNT://" & info.domainShortName & "/Staff Managers")

userWinNTPath = "WinNT://" & info.domainShortName & "/" & newManager.samAccountName

if managerGroup.isMember(userWinNTPath) then exit function

managerGroup.add userWinNTPath

managerGroup.setInfo

end function

Метод удаления пользователя из группы очень похож на предыдущий. Главное отличие – если проверка показывает, что пользователь содержится в группе менеджеров по персоналу, он из нее удаляется.

Листинг 5. Отмена полномочий менеджера по персоналу для пользователя

function makeNotManager

dim managerGroup, userWinNTPath

set managerGroup = getObject("WinNT://" & info.domainShortName & "/Staff Managers")

userWinNTPath = "WinNT://" & info.domainShortName & "/" & user.samAccountName

if managerGroup.isMember(userWinNTPath) then

managerGroup.remove userWinNTPath

managerGroup.setInfo

end if

end function

Таким образом, группа Staff Managers будет всегда содержать действительный список менеджеров по персоналу организации.

Автоматизация делегирования

Функции делегирования и его отмены реализованы. Теперь остается только обеспечить их автоматическое выполнение при смене руководителя подразделения (см. рисунок).

Назначение руководителя подразделения

Назначение руководителя подразделения

Подробно реализация обработки событий на основе сценариев была рассмотрена в [3], поэтому ограничусь поверхностным описанием. В этом случае потребуется обрабатывать события изменения объекта организационной единицы (__InstanceModificationEvent).

Для фильтрации событий будет использоваться специальный язык WQL [4] (WMI Query Language). Он основан на SQL (точнее, является подмножеством структурированного языка запросов) и во многом повторяет его синтаксис. WQL содержит только оператор SELECT, модифицированный для работы с WMI (Windows Management Instrumentation). Особенности языка будут рассмотрены в статье по мере его использования.

Сначала рассмотрим сценарий установки обработчика событий. Для этого требуется создать и соответствующим образом проинициализировать объекты трех классов

  •  __EventFilter. С помощью объекта этого класса операционная система ставится в известность, какие именно события должны обрабатываться. Для этого используется запрос на языке WQL. Для нашего случая запрос будет иметь вид:

 SELECT * FROM __InstanceModificationEvent "WITHIN 5 WHERE TargetInstance ISA 'ds_organizationalunit'

Это значит, что требуется обрабатывать события класса __InstanceModificationEvent для объектов типа ds_organizationalunit, наличие событий должно проверяться раз в 5 секунд.

Объект этого класса инкапсулирует реакцию на событие и позволяет выполнить произвольный сценарий. Текст выполняемой программы должен быть сохранен в поле ScriptText.

  •   __FilterToConsumerBinding. Класс, объект которого используется для связи объектов предыдущих двух типов. Инициализация его полей включает механизм слежения за событиями. Удаление объекта этого класса приводит к прекращению обработки соответствующего события.

Сначала рассмотрим функцию, устанавливающую слушателя на событие. Первый ее параметр – название операции. Используется при формировании имен вспомогательных классов, чтобы они были нагляднее. Второй вариант – имя события, обработчик которого создается. Третий – имя файла с кодом обработки события.

Const FOR_READING = 1

Function addListener(sOperation, sEvent, sClass, sScript)

Сначала создается объект для работы с файловой системой. Текст сценария, выполняемого в ответ на событие, для удобства будет храниться в отдельном файле и загружаться при установке обработчика.

Set fso = createObject("Scripting.FileSystemObject")

path = "\\.\root\directory\LDAP"

Set objSWbemServices = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!" & path)

Затем подключаемся к классам, экземпляры которых будем создавать впоследствии. Классы __EventFilter и __FilterToConsumerBinding являются стандартными и уже присутствуют в пространстве имен root\directory\LDAP.

Set eventFilterClass = objSWbemServices.Get("__EventFilter")

Set consumerClass = objSWbemServices.Get("ActiveScriptEventConsumer")

Set bindingClass = objSWbemServices.Get("__FilterToConsumerBinding")

ActiveScriptEventConsumer нужно отдельно скомпилировать в этом пространстве имен с помощью команды:

mofcomp -N:root\directory\LDAP %SYSTEMROOT%\system32\wbem\scrcons.mof

Затем с помощью метода SpawnInstance_ создаются экземпляры каждого класса и инициализируются необходимые поля. Вызов метода Put_ приводит к сохранению экземпляра в постоянном хранилище.

Инициализируем класс-фильтр. Основная настройка здесь – текст запроса на языке WQL, в котором указывается, события какого типа требуется обрабатывать. С помощью конструкции WITHIN 5 системе предписывается проверять очередь событий раз в 5 секунд. Операция перевода человека в другой отдел достаточно длительная, поэтому значение может быть и больше.

set userFilter = eventFilterClass.SpawnInstance_()

userFilter.Name = sOperation & "Filter"

userFilter.QueryLanguage = "WQL"

userFilter.Query = "SELECT * FROM " & sEvent & " " & "WITHIN 5 WHERE TargetInstance ISA '" & sClass & "'"

userFilter.EventNamespace = "root\directory\LDAP"

userFilter.Put_()

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

Set srcFile = fso.OpenTextFile(sScript, FOR_READING)

set userConsumer = consumerClass.SpawnInstance_()

userConsumer.Name = "Run" & sOperation & "Script"

userConsumer.ScriptText = multyLine2SingleString(srcFile.readAll)

userConsumer.ScriptingEngine = "VBScript"

userConsumer.Put_()

srcFile.close

set userBinder = bindingClass.SpawnInstance_()

userBinder.Filter = "__EventFilter.Name=" & chr(34) & sOperation & "Filter" & chr(34)

userBinder.Consumer = "ActiveScriptEventConsumer.Name=" & chr(34) & "Run" & sOperation & "Script" & chr(34)

После выполнения следующей строки начнется обработка событий изменения объектов организационных единиц.

userBinder.Put_()

end function

Function multyLine2SingleString(multyLine)

multyLine2SingleString = replace(multyLine, vbCrLf, " : ")

end Function

Функции установки слушателей событий нужно сохранить в отдельном файле с расширением vbs. В начале файла будут содержаться их вызовы.

addListener "OUModificate", __InstanceModificationEvent", "ds_organizationalunit", "onOUModification.vbs_"

addListener "CommandSeed", "__InstanceCreationEvent", "ads_usermovecommand", "onCommand.vbs_"

Функция вызывается дважды. Первый вызов устанавливает обработчик события смены руководителя подразделения (см. рисунок). Рассмотрим содержимое файла onOUModification.vbs_. Расширение файла может быть любым, поскольку используется он только для хранения текста сценария. Сам по себе он не запускается.

Итак, цель – добиться того, чтобы при смене руководителя подразделения ему автоматически делегировались полномочия манипулирования пользовательскими объектами. У предыдущего руководителя эти полномочия нужно соответственно отнять. Реализуем эту функциональность (далее приводится содержание файла onOUModification.vbs_, т.е. сценарий, выполняющийся при смене менеджера организационной единицы).

В сценариях обработки событий можно использовать экземпляр объекта текущего события, ссылка на которых хранится в переменной targetEvent. Создавать или инициализировать ее вручную не нужно. При обработке события изменения имеется доступ как к текущему (TargetInstance), так и к предыдущему (Previous Instance) состояниям объекта. Это очень удобно, так как таким образом можно получить информацию о новом и старом менеджерах. Собственно, это и делается в первых двух строках сценария.

newManager = targetEvent.TargetInstance.DS_managedBy

oldManager = targetEvent.PreviousInstance.DS_managedBy

Затем производится проверка, а изменился ли менеджер. Дело в том, что событие этого типа возникает при изменении любого свойства организационной единицы (переименовании, например), продолжать же данный сценарий нужно, только если изменился менеджер.

if newManager <> oldManager then

Сначала создается объект основного класса надстройки, разрабатываемый в ходе этой статьи, который инкапсулирует основные операции надстройки.

set engine = createObject("UserMove.Engine")

Затем сохраняем путь к организационной единице в переменной ouPath:

ouPath = targetEvent.TargetInstance.ADSIPath

if oldManager <> "" then

Если у этого подразделения был другой менеджер, аннулируем его права.

engine.undelegateOU ouPath, "LDAP://" & oldManager

end if

Затем осуществляется делегирование прав манипуляции пользовательскими объектами новому менеджеру.

if newManager <> "" then

Если новый менеджер назначен (возможен сброс этого поля, когда старое значение удаляется, а новое не назначается), ему предоставляются необходимые права.

engine.delegateOU ouPath, "LDAP://" & newManager

end if

end if

Теперь автоматическое делегирование прав работает. Достаточно назначить менеджера подразделения с помощью оснастки Active Directory Users and Computers (см. рисунок), чтобы ему были делегированы полномочия работы с объектами пользователей в рамках данного подразделения.

Далее проанализируем файл, который содержит обработчик событий, связанных с объектами команд onCommand.vbs_:

set cmd = getObject(targetEvent.TargetInstance.ADSIPath)

createObject("UserMove.Engine").dispatchCommand cmd

Он содержит две строчки. Потому что весь код реальной обработки инкапсулирован в методе dispatchCommand основного класса надстройки UserMove.Engine. Его реализации и будет посвящена следующая часть статьи.

Set objWIMService = _ GetObject("winmgmts:\\.\root\directory\LDAP")

Сначала удаляются объекты классы __FilterToConsumer Binding. Они не имеют имен, поэтому для подключения к ним используется специальный запрос на получение всех объектов, ссылающихся на фильтр удаляемого события (__EventFilter). Результат выборки обходится, и все найденные объекты удаляются из хранилища.

Set objList = objWIMService.ExecQuery("references of {__EventFilter.Name='OUModificateFilter'}")

For each objInst in objList

objInst.Delete_

Next

Операции выше достаточно для прекращения обработки событий. Ее можно применять для временного отключения надстройки. Однако для полного удаления всех следов нужно удалить и оставшиеся два объекта. У них уже есть отличительные имена, поэтому операция удаления заключается в привязке на основе имени и вызова метода obj.Delete_:

Set obj = GetObject("winmgmts:\\.\root\directory\LDAP:” & ”ActiveScriptEventConsumer='RunOUModificateScript'")

obj.Delete_

Set obj = GetObject("winmgmts:\\.\root\directory\LDAP:” & & ”__EventFilter='OUModificateFilter'")

obj.Delete_

Обработчик команд

В этом разделе будет описан еще ряд методов основного класса надстройки UserMove.Engine. Здесь основное внимание сосредоточим на обработке объектов-команд. Для этого будет использоваться единственный открытый метод dispatchCommand и несколько закрытых вспомогательных.

Каждая команда имеет поле своего типа. Тип команды – строка. Все они определены в файле класса надстройки посредством ввода следующих констант.

Команда начала перемещения:

Const START_MOVE_COMMAND = "StartMove"

Команда отказа принять переводимого сотрудника в отдел:

Const DENY_COMMAND = "Deny"

Команда подтверждения перевода сотрудника:

Const ACCEPT_COMMAND = "Accept"

Команда отмены операции перевода:

Const ROLLBACK_COMMAND = "RollBack"

function dispatchCommand(cmd)

Здесь используется обработка ошибок, поскольку преждевременное завершение функции может привести к «потерям» объектов пользователей. В кавычках это слово потому, что физически удален объект пользователя не будет, лишь перестанет быть видимым. Кроме того, сбой в этой подпрограмме без обработки ошибок может привести к переводу надстройки в противоречивое состояние. Поэтому в самом начале функции используется директива продолжения выполнения сценария, несмотря на возникающие ошибки. В конце анализируется переменная специальная err – если она не равна нулю, ошибки были. В этом случае обрабатываемая команда отменяется с помощью специального метода resetCommand.

on error resume next

Далее команда проверяется на корректность с помощью метода checkIntegrity. Некорректная команда отменяется.

if not checkIntegrity(cmd) then

resetCommand cmd

exit function

end if

Если команда успешно прошла проверку, вызывается ее обработчик. Для каждого типа команд существует свой метод обработки. Далее все они будут рассмотрены.

select case cmd.userMoveID

case START_MOVE_COMMAND:

dispatchStartMoveCommand cmd

case ACCEPT_COMMAND:

dispatchAcceptCommand cmd

case DENY_COMMAND:

dispatchDenyCommand cmd

case ROLLBACK_COMMAND:

dispatchRollbackCommand cmd

default:

resetCommand cmd

end select

if err<>0 then resetCommand cmd

end function

Сначала рассмотрим метод проверки целостности команды. Это важная часть надстройки, которая относится к функциональности сервера. Сами команды создаются на рабочих станциях клиентов (менеджеров по персоналу). Поэтому существует вероятность фальсификации этих объектов. Выполняться же должны только корректные команды. Функция checkIntegrity возвращает значение логического типа. Возвращает истину, если команда прошла проверку, и ложь в противном случае.

function checkIntegrity(cmd)

Функция может работать с не вполне корректными объектами, поэтому возможны ошибки, которые не должны прервать ее выполнение.

On Error resume next

dim owner, executor, stOwner

Изначально предполагаем худшее – команда не прошла проверку.

checkIntegrity = false

Далее получаем объект владельца команды. Поле userMoveExecutor должно указывать на этого же пользователя – менеджера по персоналу, создавшего команду. То есть команда может быть создана с использованием одной учетной записи, а в поле userMoveExecutor установлен указатель на другую. Установить поле легко, поменять владельца объекта – сложнее (для этого требуются права администратора).

stOwner = cmd.get("ntsecuritydescriptor").owner

set owner = getObject("WinNT://" & replace(stOwner,"\", "/"))

set executor = getObject(cmd.userMoveExecutor)

if err <> 0 then exit function

if owner.class = "Group" then

Если владелец объекта команды – группа, пользователь, создавший ее, должен быть членом этой группы.

checkIntegrity = owner.isMember("WinNT://" & info.domainShortName & "/" & executor.samAccountName)

elseif owner.class = "User" then

checkIntegrity = (executor.samAccountName = owner.name)

end if

end function

Метод проверяет целостность объекта. Имеет ли право пользователь пользоваться командой, проверяется при ее выполнении.

Метод «Сброс команды» удаляет ее объект. Единственное исключение – команда начала перемещения, которая содержит в себе объект пользователя. В этом случае перед удалением объект пользователя извлекается в текущую организационную единицу. Поскольку эта ситуация потенциально опасна, профиль пользователя отключается.

Листинг 6. Сброс некорректной команды

function resetCommand(cmd)

On error resume next

dim ou

set ou = getObject(cmd.parent)

if cmd.class = START_MOVE_COMMAND_CLASS then

dim user

for each user in cmd

user.accountDisabled = true

user.setInfo

ou.moveHere user.ADSPath, vbNullString

next

end if

ou.delete cmd.class, cmd.name

end function

Начало перемещения

Перейдем к рассмотрению непосредственных обработчиков команд. Сначала команда начала перемещения. Логика ее работы подробно описана в первой части статьи [2]. Единственный параметр метода – объект команды, которую нужно выполнить. Проверку на целостность к этому моменту команда уже прошла. Здесь используется ряд вспомогательных методов, реализация которых будет рассмотрена позже.

function dispatchStartMoveCommand(cmd)

dim chair, room, whomObject, parentOU

Сначала создается объект стула ожидания в целевом подразделении.

set chair = createChair(cmd)

for each whomObject in cmd

Затем объект пользователя, инкапсулированный в команду, перемещается в объект стула ожидания.

chair.moveHere whomObject.ADSPath, vbNullString

next

set parentOU = getObject(cmd.parent)

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

set room = getWaitingRoom(parentOU)

for each whomObject in chair

Функция createBackLink создает эту самую обратную ссылку:

createBackLink room, whomObject.ADSPath

В конце объект выполненной команды удаляется.

parentOU.delete START_MOVE_COMMAND_CLASS, "CN=cmd_" & START_MOVE_COMMAND & "_" & whomObject.samAccountName

next

end function

Рассмотрим использованные при обработке дополнительные методы. Первой использовалась функция создания стула ожидания.

function createChair(cmd)

dim chairNumber, room

Свойство userMoveTo команды содержит указатель на организационную единицу – пункт конечного назначения. Указатель на комнату ожидания этого подразделения снова получаем, используя функцию getWaitingRoom.

set room = getWaitingRoom(getObject(cmd.userMoveTo))

Объекты стульев пронумерованы, чтобы избежать совпадений имен. Они называются chair_1, chair_2 и так далее. Метод getNextNumber позволяет получить следующий свободный номер стула в заданной комнате.

chairNumber = getNextNumber(room, CHAIR_CLASS)

Теперь можно создать объект стула. Практически все основные свойства перемещения (инициатор, время и др.) копируются из объекта команды.

set createChair = room.create(CHAIR_CLASS, "CN=chair_" & chairNumber)

createChair.userMoveFrom = cmd.userMoveFrom

createChair.userMoveWho = cmd.userMoveWho

createChair.userMoveComment = cmd.userMoveComment

createChair.userMoveWhen = cmd.userMoveWhen

createChair.userMoveDisabled = cmd.userMoveDisabled

После выполнения следующей строки новый объект стула ожидания будет создан и готов к перемещению в него пользователя.

createChair.setInfo

end function

Далее рассмотрим функцию получения объекта комнаты ожидания, прикрепленного к заданной организационной единице. Если комнаты ожидания нет, метод создает ее. Единственный параметр – целевое подразделение.

function getWaitingRoom(ou)

dim el

Устанавливаем фильтр, чтобы в перебор с помощью цикла for each попали только объекты типа «Комната ожидания» (на самом деле в этой надстройке используется только одна комната ожидания для каждой организационной единицы).

ou.filter = Array(ROOM_CLASS)

for each el in ou

set getWaitingRoom = el

Если комната ожидания найдена, функция возвращает ссылку на нее и завершает работу.

exit Function

next

В противном случае объект комнаты ожидания сначала создается.

set getWaitingRoom = ou.create(ROOM_CLASS, "CN=waiting_room")

getWaitingRoom.setInfo

end function

Также при создании стула использовалась функция определения первого свободного номера объекта заданного типа в конкретном контейнере. Рассмотрим ее подробнее. Функция позволяет получить следующий номер для объектов любого класса (имя типа передается вторым параметром), которые именуются в виде <имя>_<номер>.

function getNextNumber(room, className)

room.Filter = Array(className)

dim i, n

getNextNumber = 0

Реализация достаточно простая. Сначала для контейнера устанавливается фильтр, чтобы в перебор посредством for each попадали только объекты нужного типа. Затем получается номер каждого объекта (функция getUnderlinedNumber). Функция определяет максимальный встреченный номер и возвращает его, увеличив на единицу.

for each i in room

n = getUnderlinedNumber(i.cn)

if n > getNextNumber then getNextNumber = n

next

getNextNumber = getNextNumber + 1

end function

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

Листинг 7. Извлечение номера объекта из его имени

function getUnderlinedNumber(src)

dim pos

pos = instr(1, src, "_", vbTextCompare)

if pos > 0 then

getUnderlinedNumber = CInt(right(src, len(src) - pos))

end if

end function

Ну и в конце этой части статьи рассмотрим последнюю вспомогательную функцию создания ссылки на перенесенный объект. Напомню, что ссылка в данной надстройке – объект, содержащий единственное поле – путь к объекту Active Directory. Схема именования используется такая же, как и для стульев ожидания: link_<номер ссылки>. Метод получает два параметра: организационную единицу, откуда осуществляется перемещение, и собственно ссылку. Реализация довольно очевидна – с помощью getNextNumber определяется первый свободный номер, затем создается экземпляр класса ссылки, инициализируется и сохраняется.

Листинг 8. Создание обратной ссылки

function createBackLink(room, backLink)

dim linkNumber

linkNumber = getNextNumber(room, LINK_CLASS)

set createBackLink = room.create(LINK_CLASS, "CN=link_" & linkNumber)

createBackLink.userMoveLink = backLink

createBackLink.setInfo

end function

Заключение

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

  1. Андросов В. Делегирование прав на перемещение учетных записей пользователей в Active Directory. Часть 1. Постановка задачи. //Системный администратор, №3, 2009 г. – С. 16-21.
  2. Андросов В. Делегирование прав на перемещение учетных записей пользователей в Active Directory. Часть 2. Реализация основных функций. //Системный администратор, №4, 2009 г. – С. 24-30.
  3. Андросов В. Синхронизация ACL и структуры организации. Часть 3. //Системный администратор», №2, 2008 г. – С. 82-87.
  4. msdn.microsoft.com.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru