Импорт отделов и сотрудников из Active Directory

Требования к учетной записи Active Directory

Клиентская учетная запись в домене Active Directory (AD), предназначенная для работы с LDAP-аутентификацией и импортом должна иметь следующие права:

  • совершение LOGON на сервере-контроллере домена, который указывается в качестве сервера аутентификации (импорта);
  • чтение OU, из которого импортируется оргструктура и нижележащих OU и объектов в них.

Настройка системы для импорта оргструктуры из Active Directory

В классах "Отдел" (ou) и "Сотрудник" (employee) необходимо создать пользовательский атрибут для записи идентификатора из AD с параметрами:

  • название: "Идентификатор в AD";
  • тип значения: "Строка";
  • код: objectGUID.

Соответствие атрибутов класса "Отдел" (ou) с AD

 
Атрибуты (коды) класса "Отдел" (ou) в системе Коды атрибутов в AD Комментарии
"Тип объекта" (metaClass)*   Задается вручную в файле конфигурации (параметр ouMetaClass)
"Дата создания" (creationDate)* whenCreated  
"Название" (title)* name  
"Идентификатор в AD" (objectGUID)* objectGUID

Пользовательский атрибут.

Поддерживается работа с атрибутом objectGUID типа "DirectoryString" (строки с поддержкой Unicode).

Тип OctetString (шестнадцатеричный формат) не поддерживается

"Родитель" (parent) parent  

Атрибуты, отмеченные звездочкой, обязательны для заполнения.

Соответствие атрибутов класса "Сотрудник" (employee) с AD

 
Атрибуты (коды) класса "Сотрудник" (employee) в системе Коды атрибутов в AD Комментарии
"Тип объекта" (metaClass)*   Задается вручную в файле конфигурации (параметр employeeMetaClass)
"Адрес электронной почты" (email) mail  
"Дата создания" (creationDate)* whenCreated  
"Идентификатор в AD" (objectGUID)* objectGUID

Пользовательский атрибут.

Поддерживается работа с атрибутом objectGUID типа "DirectoryString" (строки с поддержкой Unicode).

Тип OctetString (шестнадцатеричный формат) не поддерживается

"Имя" (firstName) givenName  
"Картинка пользователя" (image) thumbnailPhoto см. Импорт изображения пользователя
"Лицензия" (license)*   Заполняется значением по умолчанию, настроенным в классе/типе
"Наименование" (title)*   Атрибут формируется автоматически из значений атрибутов с кодами lastName, firstName, middleName: Фамилия+Имя+Отчество
"Номер домашнего телефона" (homePhoneNumber) homePhone  
"Номер мобильного телефона" (mobilePhoneNumber) mobile  
"Отдел" (parent)* parent  
"Отчество" (middleName)   Вычисляется из атрибута displayName
"Фамилия" (lastName)* sn  

Описание особенностей импорта отделов и сотрудников из AD

Архивирование и восстановление из архива:

  • Если в AD есть отдел или сотрудник, который в системе находится в архиве, то при импорте из AD отдел или сотрудник восстанавливается из архива. При восстановлении из архива сотрудники помещаются в отдел, указанный в соответствующем атрибуте в AD.
  • Если в AD нет отдела или сотрудника, а в системе он не архивный, то отдел или сотрудник в системе помещается в архив.

Импорт изображения пользователя

Импорт изображения пользователя:

  • Если параметр mode = CREATE, то в атрибут "Картинка пользователя" (image) записывается картинка (картинки) из атрибута thumbnailPhoto;
  • Если параметр mode = UPDATE, то картинки в атрибутах "Картинка пользователя" (image) и thumbnailPhoto сравниваются:
    • если картинки в атрибутах одинаковые, то картинка не заменяется;
    • если атрибут "Картинка пользователя" (image) пуст или картинки отличаются, то в атрибут "Картинка пользователя" (image) записывается картинка (картинки) из атрибута thumbnailPhoto;
    • если атрибут "Картинка пользователя" (image) не пуст, а атрибут thumbnailPhoto пуст, то поведение определяется конвертером <ad-image-converter> (параметр "eraseOld").

Особенности загрузки даты из AD

В AD даты хранятся в нестандартном формате и их нужно конвертировать из integer8 в Date через script-converter.

Пример

accountExpires — дата истечения срока действия учетной записи. Это значение представляет число 100-наносекундных интервалов с 1 января 1601 г. (UTC).

Значение 0 или 0x7FFFFFFFFFFFFFFF (9223372036854775807) указывает, что срок действия учетной записи не истекает.

Для учета часового пояса AD нужно корректно указывать TimeZone.getTimeZone('код часового пояса').

Copy
<attr name="accountExpires" column="accountExpires">
            <script-converter>
               <![CDATA[
               //Преобразование из integer8 в Date
               import java.util.GregorianCalendar;
               import java.util.TimeZone;
               if(value in ['0', '9223372036854775807'])
               {
                 return null;
               }
               long ms = Long.valueOf(value) / 10000 - 11644495200000L;
               GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone('UTC'));
               calendar.setTimeInMillis(ms);
               return calendar.getTime();
               ]]>
            </script-converter>
</attr>

Базовая конфигурация импорта из AD

Предопределенные параметры конфигурации — некоторые константные величины, которые могут использоваться в конфигурации импорта как значения по умолчанию для параметров некоторых тегов, если они не заданы в явном виде.

Предопределенные параметры конфигурации

Предопределенные параметры конфигурации <parameter>:

  • importRootUUID — UUID корневого объекта в SMP, в иерархии которого необходима архивация объектов, не участвующих в импорте. Используется в параметре hierarchy-root (тег <remove-customizer>.

    <parameter name="importRootUUID">root$101</parameter>

    где root$101 — уровень компании, архивировать все объекты в компании, не участвующие в импорте.

  • rootDN — код отдела, являющегося корневым отделом импорта из Active Directory

    <parameter name="rootDN">DC=bank,DC=ru</parameter>

  • metaClass — код метакласса импортируемых объектов в SMP:

    <parameter name="MetaClass">ou$ou</parameter>

  • idHolder — код атрибута объекта, в котором хранится внешний идентификатор в SMP:

    <parameter name="IdHolder">objectGUID</parameter>

Расширения базовой конфигурации

Расширения базовой конфигурации:

  • Исключение при импорте из AD определенных узлов с отделами и сотрудниками.

    Copy
    <!-- distingushedName (DN), которые будут проигнорированы (они и все вложенные объекты не будут проимпортированы). -->
    <!-- <parameter name="ignoredOU1">OU=Builtin,DC=bank,DC=ru</parameter> -->
    <!-- <parameter name="ignoredOU2">OU=Class,DC=bank,DC=ru</parameter> -->
    <ldap-data-source id-column="objectGUID" 
    check-user-disabled="false" import-root="true" full-domain="true" ">

    <ignored-postfix>${ignoredOU1}</ignored-postfix> 
    <!-- Не будут импортированы все объекты, чей DN заканчивается одной из строк.-->
    <!-- В перечислении недопустимы лишние пробелы до и после запятых.-->
    <!-- <ignored-postfix>${ignoredOU1}</ignored-postfix> -->
    <!-- <ignored-postfix>${ignoredOU2}</ignored-postfix> -->
    </ldap-data-source>
  • Импорт отделов и (или) сотрудников нескольких типов. Для импорта отделов и (или) сотрудников разных типов используется script-metaclass-resolver.

    При использовании script-metaclass-resolver, необходимо удалить или закомментировать constant-metaclass-resolver.

    В параметре MetaClass указать код класса "Отдел" (ou) / "Сотрудник" (employee).

    Copy
    <!-- <script-metaclass-resolver><![CDATA[ -->
    <!-- def caseCode; -->
    <!-- //Определяем код типа отдела по какому-нибудь правилу -->
    <!-- return 'ou$' + caseCode; -->
    <!-- ]]>
    </script-metaclass-resolver> -->
  • Импорт отделов и сотрудников из нескольких корневых отделов одного источника.

    Для импорта необходимо:

    • указать каждый отдел (OU) в теге <parameter> (вложен в <config>) в отдельном параметре;

    • указать каждый параметр в теге <root-element> для каждого тега <ldap-data-source>.

    Copy
    <!-- Задает код отдела являющегося корневым отделом импорта -->
    <parameter name="rootDN">OU=First,OU=Partners,OU=ServiceAccounts,DC=domain,DC=local</parameter>
    <parameter name="rootDN2">OU=Second,OU=Users,OU=ServiceAccounts,DC=domain,DC=local</parameter>
    ...
    <!-- Импорт отделов -->
    <class name="ou" threads-number="1">
    ...
            <ldap-data-source>
    ...
                <root-element>${rootDN}</root-element>
                <root-element>${rootDN2}</root-element>
    ...
    </ldap-data-source>
    ...
    </class> 

        <!-- Импорт сотрудников -->
        <class name="employee" threads-number="1">
    ...
            <ldap-data-source>
    ...
                <root-element>${rootDN}</root-element>
                <root-element>${rootDN2}</root-element>
    ...
    </ldap-data-source>
    ...
    </class>

Пример базовой конфигурации импорта отделов и сотрудников из AD

Copy
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../../target/classes/advimport/schema1.xsd"
save-log="true" threads-number="1">
<mode>CREATE</mode>
<mode>UPDATE</mode>

<!-- Предопределенные параметры конфигурации импорта -->
<parameter name="importRootUUID">root$101</parameter>
<parameter name="rootDN">DC=bank,DC=ru</parameter>
<parameter name="ouMetaClass">ou$ou</parameter>
<parameter name="ouIdHolder">objectGUID</parameter>
<parameter name="employeeMetaClass">employee$employee</parameter>
<parameter name="employeeIdHolder">objectGUID</parameter>
<parameter name="connectionCode">ldap</parameter>

<!-- DN, которые будут проигнорированы (они и все вложенные объекты не будут проимпортированы). -->
<!-- <parameter name="ignoredOU1">OU=Builtin,DC=bank,DC=ru</parameter> -->
<!-- <parameter name="ignoredOU2">OU=Class,DC=bank,DC=ru</parameter> -->

<!-- Импорт отделов -->
<class name="ou" threads-number="1" >
<parameter name="metaClass">${ouMetaClass}</parameter>
<parameter name="idHolder">${ouIdHolder}</parameter>

<ldap-data-source id-column="objectGUID" check-user-disabled="false" import-root="true" full-domain="true">

<column name="objectGUID" src-key="objectGUID" />
<column name="parent" src-key="parent"/>
<column name="name" src-key="name" />
<column name="whenCreated" src-key="whenCreated" />

<connection-code>${connectionCode}</connection-code>
<root-element>${rootDN}</root-element>
<import-tag>ou</import-tag>
<import-tag>dc</import-tag>
<!-- Не будут импортированы все объекты, чей DN заканчивается одной из строк -->
<!-- <ignored-postfix>${ignoredOU1}</ignored-postfix> -->
<!-- <ignored-postfix>${ignoredOU2}</ignored-postfix> -->
</ldap-data-source>

<hierarchical-filter parent-column="parent"/>
<constant-metaclass-resolver />
<!-- Если необходимо импортировать отделы разных типов, то нужно использовать script-metaclass-resolver, 
при этом constant-metaclass-resolver необходимо удалить или закомментировать. 
Также необходимо в параметре ouMetaClass указать код класса "Отдел" -->
<!-- <script-metaclass-resolver><![CDATA[ -->
<!-- def caseCode; -->
<!-- //Определяем код типа отдела по какому-нибудь правилу -->
<!-- return 'ou$' + caseCode; -->
<!-- ]]>
</script-metaclass-resolver> -->

<object-searcher attr="${idHolder}" />
<attr name="${idHolder}" column="objectGUID" />
<attr name="title" column="name" />
<attr name="parent" column="parent">
<object-converter metaclass="ou" attr="${idHolder}" required="false"/>
</attr>
<attr name="creationDate" column="whenCreated">
<!-- Конвертор дат с указанием часового пояса, в котором приходит дата в импортируемых данных -->
<datetime-converter format="yyyyMMddHHmmss'.0Z'" time-zone="GMT+0:00"/>
</attr>
<attr name="removed" default-value="false" />
<remove-customizer metaclass="ou" attr="${ouIdHolder}" />
</class>

<!-- Импорт сотрудников -->
<class name="employee" threads-number="1" >
<parameter name="metaClass">${employeeMetaClass}</parameter>
<parameter name="idHolder">${employeeIdHolder}</parameter>

<ldap-data-source id-column="objectGUID" check-user-disabled="true" import-root="true" full-domain="true">

<column name="objectGUID" src-key="objectGUID" />
<column name="parent" src-key="parent" />
<column name="whenCreated" src-key="whenCreated" />
<column name="givenName" src-key="givenName" />
<column name="sAMAccountName" src-key="sAMAccountName" />
<column name="homePhone" src-key="homePhone" />
<column name="mobile" src-key="mobile" />
<column name="displayName" src-key="displayName" />
<column name="mail" src-key="mail" />
<column name="sn" src-key="sn" />
<column name="thumbnailPhoto" src-key="thumbnailPhoto" />

<connection-code>${connectionCode}</connection-code>
<root-element>${rootDN}</root-element>
<import-tag>cn</import-tag>
<!-- Поскольку некоторые отделы и все вложенные отделы не импортированы, 
не нужно импортировать и сотрудников вложенных в них -->
<!-- <ignored-postfix>${ignoredOU1}</ignored-postfix> -->
<!-- <ignored-postfix>${ignoredOU2}</ignored-postfix> -->
</ldap-data-source>

<hierarchical-filter parent-column="parent" />
<constant-metaclass-resolver />
<!-- Если необходимо импортировать сотрудников разных типов, 
то нужно использовать script-metaclass-resolver. 
При этом constant-metaclass-resolver необходимо удалить или закомментировать. 
Также необходимо в параметре employeeMetaClass указать код класса "Сотрудник". -->
<!-- <script-metaclass-resolver><![CDATA[ -->
<!-- def caseCode; -->

<!-- //Определяем код типа сотрудника по какому-нибудь правилу -->
<!-- return 'employee$' + caseCode; -->
<!-- ]]></script-metaclass-resolver> -->

<object-searcher attr="${idHolder}" />
<attr name="${idHolder}" column="objectGUID" />
<attr name="parent" column="parent">
<object-converter attr="${employeeIdHolder}" metaclass="ou" required="true" />
</attr>
<attr name="lastName" column="sn" />
<attr name="firstName" column="givenName" />
<!-- Извлекает из атрибута displayName (ФИО) отчество сотрудника. 
Оно всегда должно быть третьим словом, но может и не присутствовать. -->
<attr name="middleName" column="displayName">        
<script-converter>
<![CDATA[
try
{
return value.split('\\s')[2];
}
catch (ArrayIndexOutOfBoundsException e)
{
return '';
}
]]>
</script-converter>
</attr>

<attr name="login" column="sAMAccountName" />
<attr name="email" column="mail" />
<attr name="homePhoneNumber" column="homePhone" />
<attr name="mobilePhoneNumber" column="mobile" />
<attr name="creationDate" column="whenCreated">
<datetime-converter format="yyyyMMddHHmmss'.0Z'" time-zone="GMT+0:00"/>
</attr>
<attr name="removed" default-value="false" />
<attr name="image" column="thumbnailPhoto">
<ad-image-converter title='picture' mimeType='image/jpg' eraseOld = 'false' />
</attr>
<remove-customizer metaclass="employee" attr="${employeeIdHolder}" />
</class>
</config>