Делегируем права на перемещение учетных записей пользователей в Active Directory Часть 4. Завершение надстройки::Журнал СА 6.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 Часть 4. Завершение надстройки

Архив номеров / 2009 / Выпуск №6 (79) / Делегируем права на перемещение учетных записей пользователей в Active Directory Часть 4. Завершение надстройки

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

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

Делегируем права на перемещение
учетных записей пользователей в Active Directory
Часть 4. Завершение надстройки

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

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

Рассмотрим обработчик команды отказа принятия пользователя. Имеется в виду случай, когда администратор организационной единицы-назначения отказывает пользователю в приеме. Подробно данный сценарий описан в первой части статьи [1].

function dispatchDenyCommand(cmd)

dim comment, ou, who

При отказе принять пользователя может быть указана причина, которая сохранится в поле комментария команды. Если его нет – в поле помещается значение «пусто» (empty).

comment = cmd.userMoveComment

if comment = "" then comment = "empty"

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

set who = getObject(cmd.userMoveExecutor)

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

set ou = getObject(getAncestor(cmd.userMoveTarget, 3))

if canHeManageOU(ou, who) then

С помощью метода canHeManageOU определяем, имеет ли право текущий пользователь «распоряжаться» в этой организационной единице и отдавать подобные команды. Если необходимых прав нет, команда выполнена не будет. Но если выполнение дошло до этого места, – все в порядке.

dim chair, newChair, user

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

set user = getObject(cmd.userMoveTarget)

set chair = getObject(getParent(cmd.userMoveTarget))

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

set newChair = createMirroredChair(chair, cmd)

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

newChair.moveHere cmd.userMoveTarget, vbNullString

createBackLink getWaitingRoom(ou), "LDAP://" & user.name & "," & newChair.distinguishedName

clearEmptyChair chair, cmd.userMoveTarget

end if

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

ou.delete DENY_COMMAND_CLASS, "CN=" & cmd.cn

end function

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

Листинг 1. Функция определения предка

function getAncestor(path, levels)

dim i

getAncestor = path

for i = 1 to levels

getAncestor = getParent(getAncestor)

next

end function

Функция создания «зеркального» стула. Используется, когда пользователя необходимо «пересадить» из комнаты ожидания целевой организационной единицы в исходную.

function createMirroredChair(srcChair, denyCmd)

dim chairNumber, room

set room = getWaitingRoom(getObject(srcChair.userMoveFrom))

chairNumber = getNextNumber(room, CHAIR_CLASS)

Функция подключается к организационной единице-источнику (ее путь содержится в свойстве команды userMoveFrom) и создает там стул ожидания, копируя все свойства текущего. Изменяется только метка времени команды.

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

createMirroredChair.userMoveFrom = getAncestor(srcChair.ADSPath, 2)

createMirroredChair.userMoveWho = denyCmd.userMoveExecutor

createMirroredChair.userMoveComment = denyCmd.userMoveComment

В первой части статьи [1] для поля со временем операции был выбран тип UTC Coded Time. Необходимые преобразования выполняет функция toUTC (см. Листинг 2).

createMirroredChair.userMoveWhen = toUTC(now)

createMirroredChair.userMoveDisabled = srcChair.userMoveDisabled

createMirroredChair.setInfo

end function

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

Листинг 2. Преобразование времени к типу UTC

Function toUTC(dat)

dim offsetMin

offsetMin = getTimeZoneOffset

toUTC= dateadd("n", offsetMin, dat)

end function

Собственно все преобразование сводится к смещению локального времени с учетом часового пояса. Смещение определяет функция getTimeZoneOffset, читая смещение из реестра (в минутах). То есть, скажем, если Киев находится в часовом поясе «+2», то смещение для получения времени по Гринвичу – минус 120 минут.

Понадобится еще функция обратного преобразования fromUTC. Ее реализация аналогична toUTC за исключением того, что полученное смещение нужно вычесть, а не добавить.

Листинг 3. Вычисление смещения времени для часового пояса

function getTimeZoneOffset()

if timeZoneOffset = "?" then

dim oShell, atb

set oShell = CreateObject("WScript.Shell")

atb ="HKEY_LOCAL_MACHINE\System\CurrentControlSet\" & "Control\TimeZoneInformation\ActiveTimeBias"

timeZoneOffset = oShell.RegRead(atb)

end if

getTimeZoneOffset = timeZoneOffset

end function

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

function clearEmptyChair(chairObj, whoWas)

dim srcOU

set srcOU = getObject(chairObj.userMoveFrom)

Сначала удаляется сам пустой стул ожидания.

chairObj.deleteObject(0)

dim room, li

set room = getWaitingRoom(srcOU)

room.filter = Array(LINK_CLASS

Затем в организационной единице-отправителе находится и удаляется ссылка на отправленный объект, которая теперь указывает в никуда. Так что «подвисших» ссылок не остается.

for each li in room

if li.userMoveLink = whoWas then

li.deleteObject(0)

exit function

end if

next

end function

Подтверждение перемещения

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

function dispatchAcceptCommand(cmd)

dim ou, chair, whom, ouFrom, who

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

set who = getObject(cmd.userMoveExecutor)

set ou = getObject(getAncestor(cmd.userMoveTarget, 3))

if canHeManageOU(ou, who) then

Далее проверяем, имеет ли право пользователь, создавший эту команду, управлять текущей организационной единицей. Если имеет – выполняем завершение перевода.

set whom = getObject(cmd.userMoveTarget)

set chair = getObject(getParent(cmd.userMoveTarget))

Исходный статус пользователя (включен или нет) нужно восстановить.

whom.accountDisabled =chair.userMoveDisabled

whom.setInfo

После этого пользователь окончательно перемещается, а стул корректно очищается.

ou.moveHere cmd.userMoveTarget, vbNullString

clearEmptyChair chair, cmd.userMoveTarget

end if

ou.delete COMMAND_CLASS, cmd.name

end function

Отмена перемещения

Также перемещение пользователя может быть отменено самим инициатором. Реализация метода никаких принципиальных отличий не имеет. Сначала происходит подключение ко всем объектам – участникам операции: перемещаемый пользователь, стул ожидания, инициатор перемещения, текущая организационная единица. Затем проверяются права создателя команды на ее выполнение. Если результаты проверки положительны, пользователь возвращается в свое подразделение, а вспомогательные объекты (стул, обратная ссылка) удаляются.

Листинг 4. Отмена перемещения

function dispatchRollbackCommand(cmd)

dim userMove, chair, srcPath, srcOU, who

set userMove = getObject(cmd.userMoveTarget)

set chair = getObject(userMove.parent)

set who = getObject(cmd.userMoveExecutor)

set srcOU = getObject(chair.userMoveFrom)

if (chair.class = CHAIR_CLASS) and canHeManageOU(srcOU, who) then

userMove.accountDisabled = chair.userMoveDisabled

userMove.setInfo

srcOU.moveHere userMove.ADSPath, vbNullString

clearEmptyChair chair, cmd.userMoveTarget

end if

srcOU.delete COMMAND_CLASS, cmd.name

end function

Создание команд

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

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

function move(whomPath, wherePath, comment)

Функции передается три параметра: кого перемещать, куда и с каким комментарием.

dim userMove, fromOU

if comment = "" then comment = "empty"

move = ""

set userMove = getObject(whomPath)

Ссылку на объект текущей организационной единицы можно получить, воспользовавшись свойством «родитель» (parent) перемещаемого объекта пользователя. Ведь пока он находится в своем подразделении.

set fromOU = getObject(userMove.parent)

if not canCurrentManageOU(fromOU) then

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

move = "Not enough right"

exit function

end if

Если прав достаточно, происходит окончательное перемещение.

move = transferUserTo(userMove, wherePath, fromOU, comment)

end function

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

function transferUserTo(userMove, wherePath, fromOU, comment)

dim toOU

transferUserTo = ""

set toOU = getObject(wherePath)

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

if canCurrentManageOU(toOU) then

toOU.moveHere userMove.ADSPath, vbNullString

transferUserTo = "User moved"

exit function

end if

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

enqueueHere userMove, fromOU, toOU, comment

transferUserTo = "User enqueued"

end function

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

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

Листинг 5. Создание команды перемещения пользователя

function enqueueHere(whomObject, fromOU, toOU, comment)

dim cmd

set cmd = fromOU.create(START_MOVE_COMMAND_CLASS, "CN=cmd_" & START_MOVE_COMMAND & "_" & whomObject.samAccountName)

cmd.userMoveID = START_MOVE_COMMAND

cmd.userMoveExecutor = "LDAP://" & info.userName

cmd.userMoveFrom = fromOU.ADSPath

cmd.userMoveWho = "LDAP://" & info.userName

cmd.userMoveComment = comment

cmd.userMoveWhen = toUTC(now)

cmd.userMoveDisabled = whomObject.accountDisabled

cmd.userMoveTo = toOU.ADSPath

cmd.userMoveTarget = whomObject.ADSPath

cmd.setInfo

whomObject.accountDisabled = true

whomObject.setInfo

cmd.moveHere whomObject.ADSPath, vbNullString

end function

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

Листинг 6. Создание команды подтверждения перемещения

function accept(whomPath)

dim cmd, ou

set ou = getObject(getAncestor(whomPath, 3))

set cmd = ou.create(COMMAND_CLASS, "CN=cmd_" & ACCEPT_COMMAND & "_" & getObject(whomPath).samAccountName)

cmd.userMoveID = ACCEPT_COMMAND

cmd.userMoveExecutor = "LDAP://" & info.userName

cmd.userMoveTarget = whomPath

cmd.setInfo

end function

Команда отказа формируется аналогично, поэтому полный текст ее инициализации приводить не буду. Называется соответствующий метод deny. В качестве параметров ему передается путь к объекту команды и комментарий, содержащий причину отказа. То же касается и создания команды отката. Метод ее создания называется rollback и принимает один параметр – объект воздействия.

Путь к родительскому контейнеру

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

В чем заключается логика работы. Объекты в Active Directory упорядочены иерархически. На самом верху иерархии находится домен. Например, для домена с именем marclar.ua путь будет выглядеть так: LDAP://DC=marklar, DC=ua. Если в рамках домена существует подразделение с именем department, которое в свою очередь содержит сотрудника ivanov, то путь к объекту пользователя будет LDAP://DN=ivanov,OU=department,DC=marklar,DC=ua. То есть путь указывается от листового элемента дерева к корню. Для пользователей используется идентификатор DN (Distinguished Name или отличительное имя), для организационных единиц – OU (Organizational Unit), для элементов названия домена – DC (Domain Component). Получение пути к родительскому контейнеру заключается в отбрасывании от текущего значения первого элемента (в нашем примере это подстрока DN=ivanov).

Ранее я использовал примитивную версию этой функции [4], которая, однако, вполне пригодна и для этой надстройки. Она находит в пути первую запятую, вырезает подстроку после нее и добавляет в начало название протокола.

Листинг 7. Определение родительского подразделения без использования регулярных выражений

function getParent(path)

getParent = “LDAP://” & right(path, len(path) - instr(1, path, ",", vbTextCompare))

end function

Рассмотрим, как то же самое можно сделать с помощью регулярных выражений. Регулярное выражение позволяет задать правило, по которому можно опознать часть строки. Для этого используется ряд специальных символов. Рассмотрим только элементы, необходимые для понимания материалов статьи. Дополнительную информацию по регулярным выражениям можно без труда найти в MSDN [5].

Логика их использования напоминает маски для файлов, где знак вопроса обозначает один любой символ, а звездочка – любое количество произвольных символов. Регулярные выражения несколько сложнее. Рассмотрим необходимые специальные символы (см. таблицу 1).

Таблица 1. Специальные символы

Символ

Описание

[]

Несколько символов в квадратных скобках. Обозначает один любой из этих символов. Например [ab] обозначает a или b

^

Отрицание. То есть [^ab] обозначает любой символ, кроме a и b

()

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

\

Обратная косая черта используется для экранирования спецсимволов. Например, если требуется указать круглую скобку именно как символ, следует записать так: \(

|

Или. Например abc|cde обозначает «подстрока abc или cde»

*

Обозначает, что выражение, стоящее до звездочки, может повториться 0 и более раз. Например, a* обозначает любое количество идущих подряд букв а (в том числе ни одной).

?

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

.

Точка. Любой символ, кроме перевода строки

Итак, напишем регулярное выражение для выделения пути родительского контейнера. Путь начинается с указания протокола LDAP://, если этой приставки нет, то соответствующая строка называется «Отличительное имя» (Distinguished Name). Чтобы наша функция работала с обоими типами строк, укажем приставку как необязательный элемент:

(?:LDAP\:\\\\)?

С помощью «?:» мы сообщаем анализатору, что строка, соответствующая этому выражению, нам не понадобится, затем следуют символы «:\\». Поскольку каждый из них является еще и специальным, все они экранированы с помощью обратной косой черты. В конце идет знак вопроса, т.е. эта подстрока может встретиться в пути один (ADSPath) или ноль (Distinguished Name) раз.

Далее описываем первый элемент пути:

(?:(?:CN|OU)=[^,]*,)?

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

Ну и заканчивается выражение остальными символами.

(.*)

То есть любое количество любых символов. Здесь используются просто круглые скобки, потому что эта подстрока нас как раз и интересует.

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

function getParent(path)

dim matches

Сначала задаем шаблон регулярного выражения.

re.pattern = "(?:LDAP\:\/\/)?(?:(?:CN|OU)=[^,]*,)?(.*)"

Затем происходит поиск соответствий.

set matches = re.execute(path)

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

getParent = "LDAP://" & matches(0).submatches(0)

end function

Используя этот подход, реализуем еще одну полезную функцию. Перевода пути к объекту в удобочитаемый для простого пользователя вид. Организация Active Directory подобна файловой системе с ее каталогами и вложенными файлами. В то же время путь здесь задается как бы «наизнанку», начиная не из корня иерархии, а от конкретного элемента.

Напишем функцию, которая представляет путь: LDAP://DN=ivanov,OU=department,DC=marklar,DC=ua в виде Marklar.ua/department/ivanov.

Функции передается путь к объекту в стиле Active Directory:

function ADSPath2Readable(path)

dim matches, i, splitter

Сначала выделяем доменное имя. Оно может состоять из одного и более доменных компонентов через запятую, каждая часть имеет вид DC=<имя доменного компонента (ИДК)>.

re.pattern = "DC=([^,]*)"

set matches = re.execute(path)

ADSPath2Readable = ""

for i = 0 to matches.count – 1

Все совпадения затем собираются в строку вида <ИДК>.<ИДК>…<ИДК>

if i = 0 then

splitter = ""

else

splitter = "."

end if

ADSPath2Readable = ADSPath2Readable & splitter & matches(i).submatches(0)

next

С помощью следующего регулярного выражения выделяются остальные элементы пути – организационные единицы (OU) и остальные объекты (DN). В отличие от доменных компонентов эти элементы требуется собирать в строку в обратном порядке, поэтому массив совпадений перебирается с конца. Между названиями добавляется прямая наклонная линия. Здесь все равно, какой символ использовать, поскольку единственное приложение данной функции – наглядное представление пути.

re.pattern = "(?:CN|OU)=([^,]*)"

set matches = re.execute(path)

for i = matches.count - 1 to 0 step -1

ADSPath2Readable = ADSPath2Readable & "/" & matches(i).submatches(0)

next

end function

Основной класс надстройки

Наконец, можно перечислить все открытые методы основного класса надстройки. Все они были реализованы ранее (см. таблицу 2).

Таблица 2. Интерфейс класса UserMove.Engine

Метод

Описание

dispatchCommand

Обработчик команд менеджеров по персоналу

canCurrentManagePath

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

canHeManagePath

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

delegateOU

Предоставление заданному пользователю прав для управления организационной единицей

undelegateOU

Лишения пользователя прав управления организационной единицей

move

Создать команду начала перемещения пользователя

accept

Создать команду подтверждения перемещения пользователя

Deny

Создать команду отказа в перемещении пользователя

Rollback

Создать команду отмены перемещения пользователя

getParent

Получить путь к родительскому контейнеру

ADSPath2Readable

Преобразовать ADSI путь к объекту Active Directory в удобочитаемый вид

toUTC

Перевести местное время во время по Гринвичу

fromUTC

Получить из времени по Гринвичу местное время

Когда класс создан, нужно не забыть зарегистрировать его в системе с помощью контекстного меню проводника «Установить (Register)». Можно сделать то же самое из командной строки:

  •  Regsvr32 UserMove.Engine.wsc – регистрация компонента;
  •  Regsvr32 /u UserMove.Engine.wsc – его удаление.

Создание общих ресурсов домена

В конце вкратце хотелось бы рассмотреть вопрос размещения исходных файлов надстройки. Для этого удобнее всего будет воспользоваться механизмом DFS (Distributed File System) [6]. С его помощью можно обращаться к файлам, полностью игнорируя их физическое расположение. То есть, например, для домена marklar.ua можно пользоваться строкой следующего вида для обращения к файлам: \\marklar.ua\files\file.txt. При этом становится не важно, на каком компьютере хранится указанный файл (на самом деле он может быть расположен сразу на нескольких машинах для повышения надежности).

Для начала нужно создать корень DFS. Физически это должна быть обычная разделяемая (shared) папка. Пусть это будет UserMoveSupport. Корень создается с использованием специальной оснастки Distributed File System (см. рис. 1).

Рисунок 1. Создание корня DFS

Рисунок 1. Создание корня DFS

После выполнения команды «New Root…» будет запущен мастер создания корневого элемента распределенной файловой системы. Тип корня должен быть «Domain Root». Дальше указываются название домена и сервера. Затем вводится имя корня – назовем его UserMoveSupport (см. рис. 2).

Рисунок 2. Имя корня

Рисунок 2. Имя корня

Когда создание корневого элемента будет завершено, нужно создать так называемые ссылки (команда «New link…», вызываемая из контекстного меню корня). Фактически нужно указать разделяемые (shared) папки, доступ к которым мы хотим обеспечить с помощью DFS (см. рис. 3).

Рисунок 3. Создание ссылки

Рисунок 3. Создание ссылки

Для окончательного создания ссылки нужно указать, имя, по которому в дальнейшем нужно будет к ней обращаться (Link name), и путь к разделяемому ресурсу (Path to target (shared folder)). Обратите внимание, что эти имена не обязательно должны совпадать (см. рис. 4).

Рисунок 4. Настройки новой ссылки

Рисунок 4. Настройки новой ссылки

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

"\\marklar.ua\UserMoveSupport\exec\"

Организация работы с приложениями

Для надстройки будет создан ряд вспомогательных hta-модулей, которые вызываются из контекстного меню оснастки Active Directory Users and Computers. HTA (HyperText Application) – особый вид приложений с графическим интерфейсом, основанным на HTML, подробно их создание будет рассмотрено в следующей части статьи. Рассмотрим общий механизм реализации такого подхода. В результате должно получиться так, как изображено на рис. 5 (для наглядности контекстные меню надстройки начинаются с приставки UserMove:).

Рисунок 5. Контекстные меню надстройки

Рисунок 5. Контекстные меню надстройки

Следующая функция добавляет новое контекстное меня в оснастку. Для этого нужно создать специальную запись в Active Directory.

function installMenu(className, id, name, scriptPath, action, locale)

Сначала подключаемся к корневому элементу Active Directory:

Set root= GetObject("LDAP://rootDSE")

Используя корневой элемент, получаем указатель на ветвь с настройками, которая называется configuration NamingContext, далее этот объект будет использоваться при формировании полного пути (ADSPath) необходимой конкретной настройки.

sConfig = root.Get("configurationNamingContext")

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

sPath = "LDAP://cn=" & className & "-Display,cn=" & locale & ",cn=DisplaySpecifiers," & sConfig

Set obj= GetObject(sPath)

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

sValue = id & "," & name & "," & scriptPath

Затем эта строка преобразуется в трехэлементный массив:

vValue = Array(sValue)

Наконец редактируется запись с надстройками. Изменить нужно ее свойство под названием adminContext Menu. Это массив, для которого мы будем использовать две операции (вид операции задается параметром action метода PutEx). Для добавления элемента используется константа ADS_PROPERTY_APPEND (3), для удаления ADS_PROPERTY_DELETE (4). Константы придется определить вручную, подсмотрев их значения в MSDN [4].

obj.PutEx action, "adminContextMenu", vValue

obj.SetInfo

end function

 Рассмотрим параметры, передаваемые функции:

  •  className. Имя класса Active Directory, к которому будет привязываться создаваемый элемент контекстного меню. В этой надстройке – user или organizationalUnit.
  •  id. Числовой идентификатор, определяющий позицию пункта меню.
  •  name. Текст меню.

Путь к сценарию, который необходимо выполнить при выборе элемента меню.

  •  action. Константа, определяющая, будет элемент меню добавлен или удален.
  •  locale. Локализация. Задается числом в шестнадцатеричной системы счисления. Для английского языка это 409, для русского – 419.

Необходимые настройки определим в виде констант.

Листинг 8. Константы инициализации надстройки

Const ADS_PROPERTY_APPEND = 3

Const ADS_PROPERTY_DELETE = 4

Const LOCALE_ENGLISH = "409"

Const LOCALE_RUSSIAN = "419"

Const prefix = "UserMove: "

Const path = "\\marklar.ua\UserMoveSupport\exec\"

 Также создадим ряд функций-оберток:

  •  installOUMenu – установка раздела меню для организационной единицы;
  •  installUserMenu – установка раздела меню для пользователя;
  • uninstallOUMenu – удаление раздела меню организационной единицы;
  • uninstallUserMenu – удаление раздела меню пользователя.

Рассмотрим только одну из этих подпрограмм, все остальные отличаются только передаваемыми в installMenu константами.

Листинг 9. Создание дополнительных пунктов меню

function installOUMenu(id, name, scriptPath, locale)

installMenu "organizationalUnit", id, name, scriptPath, ADS_PROPERTY_APPEND, locale

end function

Всего будет три приложения. Одно будет привязано к объекту пользователя и позволит начать операцию его перемещения (приложение enqueue.hta). И два нужно прикрепить к организационным единицам: просмотр списков входящих (incoming.hta) и исходящих (outcoming.hta) пользователей.

Листинг 10. Создание пунктов меню для английской локали

function installEN

installUserMenu 100, prefix & "Start", path & "enqueue.hta", LOCALE_ENGLISH

installOUMenu 100, prefix & "Outcoming", path & "outcoming.hta", LOCALE_ENGLISH

installOUMenu 110, prefix & "Incoming", path & "incoming.hta", LOCALE_ENGLISH

end Function

Функция installEN устанавливает все необходимые пункты меню для английской локализации. Аналогично создается подпрограмма для удаления этих пунктов uninstallEN а также соответствующие процедуры для русского языка uninstallRU и installRU.

Работу с двумя локализациями необходимо обеспечить, поскольку в противном случае пользователи русских версий Windows новых пунктов меню просто не увидят.

Заключение

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

  1. Андросов В. Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 1. Постановка задачи. //Системный администратор, №3, 2008 г. – С. 16-21.
  2. Андросов В. Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 2. Реализация основных функций. //Системный администратор, №4, 2008 г. – С. 24-30.
  3.  Андросов В. Делегируем права на перемещение учетных записей пользователей в Active Directory. Часть 3. Реализуем необходимые операции. //Системный администратор, №5, 2008 г. – С. 30-37.
  4. Андросов В. Реализуем нестандартные правила управления доступом на основе архитектуры организации в Windows Server 2003. //Системный администратор, №10, 2007 г. – С. 48-58.
  5. msdn.microsoft.com.
  6.  Чарли Рассел, Шарон Кроуфорд, Джейсон Джеренд. Windows server 2003 +SP1 и R2. Справочник администратора. – М.: Издательство «ЭКОМ», 2006 г. – 1424 с.

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

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

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

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

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