Предпосылки: В организации, сведения о которой ведутся в нашем каталоге, два отдела: Developers и Designers. Информация о принадлежности пользователя отделу фиксируется как значение атрибута ou в учётной записи каждого пользователя. На почтовом сервере организации обслуживаются почтовые ящики пользователей, адреса электронной почты фиксируются как значение атрибута mail в учётной записи каждого пользователя.
Задача: Требуется создать списки рассылки, которые будут использоваться почтовым сервером для организации рассылки писем всем сотрудникам организации и отдельно сотрудникам каждого подразделения. Эти списки должны постоянно отражать актуальную структуру организации.
Поскольку мы будем использовать динамическое построение списков, то проблема того, что нужно сделать в первую очередь, — модифицировать записи пользователей, настроить само наложение или добавить заготовки под динамически формируемые записи, — остро не стоит. Вне зависимости от порядка этих действий в итоге мы получим работоспособные динамические списки.
В данном примере мы покажем "прямой" порядок: учётки, наложение, списки.
На этом этапе мы приведём наш каталог в соответствие с предпосылками к нашей задаче, то есть добавим к учётным записям пользователей, которые имеются в нашем каталоге в его исходном положении, признак их принадлежности к отделу и адрес электронной почты. Для этого сформируем такой LDIF-файл:
dn: uid=antonova,ou=People,dc=mycompany,dc=ruchangetype: modifyadd: ouou: Designers-add: mailmail: antonova@mycompany.rudn: uid=ivanov,ou=People,dc=mycompany,dc=ruchangetype: modifyadd: ouou: Developers-add: mailmail: ivanov@mycompany.rudn: uid=petrov,ou=People,dc=mycompany,dc=ruchangetype: modifyadd: ouou: Developers-add: mailmail: petrov@mycompany.rudn: uid=sidorov,ou=People,dc=mycompany,dc=ruchangetype: modifyadd: ouou: Designers-add: mailmail: sidorov@mycompany.ru
Применим его к нашему каталогу и посмотрим, в каком состоянии будут наши учётки после модификации:
$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -c -f ./002-add_users_attributes.ldif Enter LDAP Password: modifying entry "uid=antonova,ou=People,dc=mycompany,dc=ru" modifying entry "uid=ivanov,ou=People,dc=mycompany,dc=ru" modifying entry "uid=petrov,ou=People,dc=mycompany,dc=ru" modifying entry "uid=sidorov,ou=People,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b ou=People,dc=mycompany,dc=ru -s one dn: uid=ivanov,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: ivanov cn: Ivan Ivanov sn: Ivanov ou: Developers mail: ivanov@mycompany.ru dn: uid=petrov,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: petrov cn: Petr Petrov sn: Petrov ou: Developers mail: petrov@mycompany.ru dn: uid=sidorov,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: sidorov cn: Sidor Sidorov sn: Sidorov ou: Designers mail: sidorov@mycompany.ru dn: uid=antonova,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: antonova cn: Antonina Antonova sn: Antonova ou: Designers mail: antonova@mycompany.ru
Отлично! Необходимые для построения динамических списков рассылки сведения помещены в каталог. Переходим ко второму этапу.
Как мы помним, в нашей сборке OpenLDAP все механизмы манипуляции данными и наложения собраны в виде динамических модулей. Поэтому, перед тем, как приступить к настройкам наложения, его требуется подгрузить в работающий сервер slapd. Как обычно, для этого мы создадим соответствующий LDIF-файл:
dn: cn=module{0},cn=configchangetype: modifyadd: olcModuleLoadolcModuleLoad: dynlist.la
Подгрузим наш модуль и убедимся, что он загружен:
$ ldapmodify -x -D cn=config -W -f ./101-add_dynlist_module.ldif
Enter LDAP Password:
modifying entry "cn=module{0},cn=config"
$ ldapsearch -xLLL -D cn=config -W -b cn=module{0},cn=config olcModuleLoad
Enter LDAP Password:
dn: cn=module{0},cn=config
olcModuleLoad: {0}back_mdb.la
olcModuleLoad: {1}dynlist.la
Можно приступать к настройкам. Для данного примера они получились очень простыми:
dn: olcOverlay=dynlist,olcDatabase={1}mdb,cn=configobjectClass: olcDynamicListolcOverlay: dynlistolcDLattrSet: groupOfURLs memberURL
К записи нашей рабочей базы данных mdb добавляется дочерняя запись наложения с объектным классом olcDynamicList. Параметры работы наложения задаются значением атрибута olcDLattrSet (строка 4). Как мы видим, у нас два разделённых пробелом параметра: имя объектного класса (groupOfURLs) и имя типа атрибута (memberURL). Наложение dynlist наполняет динамическим содержимым записи, возвращаемые в ответ на поисковый запрос LDAP. Чтобы отобрать среди всех возвращаемых записей те, которые требуется модифицировать, наложению необходимы критерии отбора. Именно их мы в данном случае и задаём. То есть запись, отобранная для возврата в ответ на поисковый запрос LDAP, будет подвержена модификации (дополнена динамическим содержимым), только если эта запись строится на объектном классе groupOfURLs и содержит атрибут memberURL. Обратите внимание, что в настройках наложения никоим образом не регламентируется собственно динамическое содержимое, то есть то, чем именно будут наполняться записи. Определение динамического содержимого отдано в ведение пользователя каталога: оно задаётся в виде параметров поискового запроса, оформленных как LDAP URI, который помещается в значение атрибута, тип которого указан в параметрах наложения (в данном случае в значение атрибута memberURL). То есть, зная правила составления LDAP URI, пользователь может настроить записи с динамическим содержимым под свою задачу.
Для нашего примера с классическими динамическими списками мы выбрали "классические" объектный класс и тип атрибута, которые указываются в примерах настройки в man-странице наложения dynlist. В стандартную схему данных сервера slapd они не входят, поэтому перед тем, как их использовать, требуется указать их определения серверу. Это делается путём добавления набора схемы данных dyngroup (входит в состав пакета OpenLDAP):
$ ldapadd -x -D cn=config -W -f /etc/ldap/schema/dyngroup.ldif
Enter LDAP Password:
adding new entry "cn=dyngroup,cn=schema,cn=config"
$ ldapsearch -xLLL -D cn=config -W -b cn=schema,cn=config 1.1
Enter LDAP Password:
dn: cn=schema,cn=config
dn: cn={0}core,cn=schema,cn=config
dn: cn={1}cosine,cn=schema,cn=config
dn: cn={2}nis,cn=schema,cn=config
dn: cn={3}inetorgperson,cn=schema,cn=config
dn: cn={4}dyngroup,cn=schema,cn=config
Заодно посмотрим, что же было в добавленном нами наборе:
$ ldapsearch -xLLL -D cn=config -W -b cn={4}dyngroup,cn=schema,cn=config -o ldif-wrap=no
Enter LDAP Password:
dn: cn={4}dyngroup,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: {4}dyngroup
olcObjectIdentifier: {0}NetscapeRoot 2.16.840.1.113730
olcObjectIdentifier: {1}NetscapeLDAP NetscapeRoot:3
olcObjectIdentifier: {2}NetscapeLDAPattributeType NetscapeLDAP:1
olcObjectIdentifier: {3}NetscapeLDAPobjectClass NetscapeLDAP:2
olcObjectIdentifier: {4}OpenLDAPExp11 1.3.6.1.4.1.4203.666.11
olcObjectIdentifier: {5}DynGroupBase OpenLDAPExp11:8
olcObjectIdentifier: {6}DynGroupAttr DynGroupBase:1
olcObjectIdentifier: {7}DynGroupOC DynGroupBase:2
olcAttributeTypes: {0}( NetscapeLDAPattributeType:198 NAME 'memberURL' DESC 'Identifies an URL associated with each member of a group. Any type of labeled URL can be used.' SUP labeledURI )
olcAttributeTypes: {1}( DynGroupAttr:1 NAME 'dgIdentity' DESC 'Identity to use when processing the memberURL' SUP distinguishedName SINGLE-VALUE )
olcAttributeTypes: {2}( DynGroupAttr:2 NAME 'dgAuthz' DESC 'Optional authorization rules that determine who is allowed to assume the dgIdentity' EQUALITY authzMatch SYNTAX 1.3.6.1.4.1.4203.666.2.7 X-ORDERED 'VALUES' )
olcObjectClasses: {0}( NetscapeLDAPobjectClass:33 NAME 'groupOfURLs' SUP top STRUCTURAL MUST cn MAY ( memberURL $ businessCategory $ description $ o $ ou $ owner $ seeAlso ) )
olcObjectClasses: {1}( DynGroupOC:1 NAME 'dgIdentityAux' SUP top AUXILIARY MAY ( dgIdentity $ dgAuthz ) )
Сначала добавляются 8 новых идентификаторов объектов, затем 3 типа атрибута (memberURL, dgIdentity и dgAuthz) и 2 объектных класса (groupOfURLs и dgIdentityAux). На данном этапе главное для нас — наличие типа атрибута memberURL и объектного класса groupOfURLs, которые используются в данном примере. Использование остальных типов атрибутов и объектного класса мы рассмотрим позже.
Наконец, добавим описанную выше запись с настройками нашего наложения и убедимся в её наличии:
$ ldapadd -x -D cn=config -W -f ./102-add_dynlist_overlay.ldif
Enter LDAP Password:
adding new entry "olcOverlay=dynlist,olcDatabase={1}mdb,cn=config"
$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one
Enter LDAP Password:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs memberURL
Наложение стало первым (индекс 0) для нашей рабочей базы данных. Пора переходить к следующему этапу.
Чтобы не нарушать стройной иерархии нашего каталога, для списков рассылки электронной почты мы заведём отдельный контейнер MailingLists. Согласно условиям задачи, составим сразу определения для трёх динамических списков рассылки: All (все сотрудники организации), Developers и Designers (сотрудники соответствующих отделов):
dn: ou=MailingLists,dc=mycompany,dc=ruobjectClass: organizationalUnitou: MailingListsdn: cn=All,ou=MailingLists,dc=mycompany,dc=ruobjectClass: groupOfURLscn: AllmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(objectClass=inetOrgPerson)dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ruobjectClass: groupOfURLscn: DevelopersmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Developers))dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ruobjectClass: groupOfURLscn: DesignersmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Designers))
Разберём подробнее первый список рассыки All (строки 5-8). Он строится на структурном объектном классе groupOfURLs (тот самый класс, на который, согласно нашим настройкам, будет срабатывать наложение dynlist). Согласно схеме данных, обязательным атрибутом данного класса является cn, что удобно при формировании RDN записи. Одним из необязательных атрибутов этого же класса является memberURL, который также фигурирует в настройках наложения dynlist. Значением данного атрибута является LDAP URI, описывающий параметры дополнительного поискового запроса LDAP в тот же каталог. Разберём его по частям.
| № | Компонент URI | В нашем случае | Пояснение |
|---|---|---|---|
| 1 | Схема URI | ldap:// | Означает, что будет выполняться запрос протокола LDAP. |
| 2 | Адрес сервера | не указан | Пустое место между вторым и третьим слешем означает, что адрес сервера не указан, и запрос будет обрабатываться на локальном сервере. |
| 3 | База поиска | ou=People,dc=mycompany,dc=ru | Поддерево, начиная с которого будут отбираться записи. |
| 4 | Возвращаемые атрибуты | Из найденных записей будут возвращены только указанные атрибуты. Если нужно указать несколько возвращаемых атрибутов, они перечисляются через запятую. Если в этом компоненте не будет указано никаких атрибутов, то будут возвращены все пользовательские атрибуты. | |
| 5 | Диапазон поиска | one | Будут рассматриваться только записи, непосредственно дочерние записи-базе поиска. |
| 6 | Поисковый фильтр | (objectClass=inetOrgPerson) | Среди всех записей, попадающих в диапазон поиска, будут отобраны только те, которые строятся на объектном классе inetOrgPerson |
Как видите, задаются все параметры стандартного поискового запроса. Итак, в нашей записи нет ничего лишнего: атрибут cn со значением All, формирующим RDN записи, и атрибут memberURL, значением которого является LDAP URI, описывающий поисковый запрос LDAP, ответные данные которого (значения атрибутов mail найденных записей) будут формировать динамическое содержимое нашего списка.
Два других списка (Developers и Designers), фактически, отличаются только дополнительными критериями отбора в поисковых фильтрах LDAP URI, задаваемых в атрибутах memberURL. По этим критериям среди всех учётных записей в ветке ou=People,dc=mycompany,dc=ru будут отбираться только учётные записи пользователей, принадлежащих соответствующим отделам.
Добавим наши динамические списки:
$ ldapadd -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./003-add_mailing_lists.ldif Enter LDAP Password: adding new entry "ou=MailingLists,dc=mycompany,dc=ru" adding new entry "cn=All,ou=MailingLists,dc=mycompany,dc=ru" adding new entry "cn=Developers,ou=MailingLists,dc=mycompany,dc=ru" adding new entry "cn=Designers,ou=MailingLists,dc=mycompany,dc=ru"
Попробуем извлечь добавленные записи. Заодно убедимся, что наложение dynlist работает:
$ ldapsearch -xLLL -b ou=MailingLists,dc=mycompany,dc=ru -s one -o ldif-wrap=no dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: All memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(objectClass=inetOrgPerson) mail: antonova@mycompany.ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: Designers memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Designers)) mail: antonova@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: Developers memberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Developers)) mail: ivanov@mycompany.ru mail: petrov@mycompany.ru
Как видно, в наших записях, кроме статически заданных нами атрибутов, появились динамически сформированные атрибуты mail со значениями, отобранными в результате выполнения дополнительного поиска.
Разберём работу наложения dynlist поэтапно:
ldapsearch, записи, отобранные согласно этим критериям, пользователю не возвращаются, а передаются на обработку наложению dynlist.groupOfURLs и имеют атрибут memberURL. В нашем случае в этот список попадут записи cn=All,ou=MailingLists,dc=mycompany,dc=ru, cn=Designers,ou=MailingLists,dc=mycompany,dc=ru и cn=Developers,ou=MailingLists,dc=mycompany,dc=ru.memberURL. Например, для записи cn=Designers,ou=MailingLists,dc=mycompany,dc=ru будет выполнен запрос с базой поиска ou=People,dc=mycompany,dc=ru, диапазоном поиска one и фильтром (&(objectClass=inetOrgPerson)(ou=Designers)).mail со значениями antonova@mycompany.ru и sidorov@mycompany.ru соответственно.Всё просто и удобно. Динамические списки будут актуальны всегда, если администратор будет своевременно корректировать учётные записи пользователей, например, когда кто-то из них переведётся в другой отдел или уволится.
Убедимся в этом. Допустим, Сидор Сидоров уволился, а Пётр Петров решил перевестись в отдел дизайна. В каталог будут внесены следующие изменения:
dn: uid=sidorov,ou=People,dc=mycompany,dc=ruchangetype: deletedn: uid=petrov,ou=People,dc=mycompany,dc=ruchangetype: modifyreplace: ouou: Designers
Внесём изменения в каталог и убедимся в корректности полученных в результате записей:
$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./004-replace_users.ldif Enter LDAP Password: deleting entry "uid=sidorov,ou=People,dc=mycompany,dc=ru" modifying entry "uid=petrov,ou=People,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b ou=People,dc=mycompany,dc=ru -s one '(|(uid=sidorov)(uid=petrov))' dn: uid=petrov,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: petrov cn: Petr Petrov sn: Petrov mail: petrov@mycompany.ru ou: Designers
Запись sidorov отсутствует в каталоге, а в атрибуте ou записи petrov указан отдел Designers. Посмотрим, что стало с динамическими списками:
$ ldapsearch -xLLL -b ou=MailingLists,dc=mycompany,dc=ru -s one mail dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: petrov@mycompany.ru dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru mail: ivanov@mycompany.ru
Всё работает, как и ожидалось.
Случается, что сотрудник организации довольно длительное время может отсутствовать на рабочем месте. Тогда его почтовый ящик скорее всего забьётся сообщениями, которые к моменту его возвращения уже утратят свою актуальность. Мы можем попытаться "блокировать" рассылку почты для таких сотрудников, и динамические списки отлично подходят для решения такой задачи.
Предположим, Тоня Антонова ушла в декретный отпуск, и мы пометили её учётную запись как заблокированную путём добавления в неё атрибута l со значением locked:
dn: uid=antonova,ou=People,dc=mycompany,dc=ruchangetype: modifyadd: ll: locked
Внесём изменения в каталог и убедимся, что всё в порядке:
$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./005-locked_antonova.ldif Enter LDAP Password: modifying entry "uid=antonova,ou=People,dc=mycompany,dc=ru" $ ldapsearch -xLLL -b dc=mycompany,dc=ru '(uid=antonova)' l dn: uid=antonova,ou=People,dc=mycompany,dc=ru l: locked
Мы можем использовать вновь полученное свойство учётных записей для модификации поисковых фильтров в LDAP URI записей динамических списков так, чтобы в результате их применения для построения динамического содержимого отбирались почтовые ящики только активных (незаблокированных) в данный момент пользователей. Составим такой LDIF-файл:
dn: cn=All,ou=MailingLists,dc=mycompany,dc=ruchangetype: modifyreplace: memberURLmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(!(l=locked)))dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ruchangetype: modifyreplace: memberURLmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Developers)(!(l=locked)))dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ruchangetype: modifyreplace: memberURLmemberURL: ldap:///ou=People,dc=mycompany,dc=ru?mail?one?(&(objectClass=inetOrgPerson)(ou=Designers)(!(l=locked)))
Как видно, в LDAP URI динамических списков всё осталось по-прежнему, за исключением фильтра отбора. В нём добавилось дополнительное условие: не рассматривать записи, в которых атрибут l имеет значение locked.
Применим наши изменения:
$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -f ./006-modify_mailing_lists.ldif Enter LDAP Password: modifying entry "cn=All,ou=MailingLists,dc=mycompany,dc=ru" modifying entry "cn=Developers,ou=MailingLists,dc=mycompany,dc=ru" modifying entry "cn=Designers,ou=MailingLists,dc=mycompany,dc=ru"
Посмотрим, что стало с динамическим содержимым списков рассылок:
$ ldapsearch -xLLL -b ou=MailingLists,dc=mycompany,dc=ru -s one mail dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru mail: petrov@mycompany.ru dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru mail: ivanov@mycompany.ru
Как видим, в списках All и Designers не перечислен почтовый адрес antonova@mycompany.ru, чего мы и добивались.
Обратите внимание, что перенастройка динамического содержимого наших списков была произведена без внесения изменений в настройки наложения dynlist в каталоге cn=config. Все изменения производятся в рабочем каталоге и могут быть выполнены любым пользователем, у которого есть соответствующие права на внесение изменений в этот каталог.
В ходе этого урока мы познакомились с настройками наложения dynlist для организации классических динамических списков. Часть настроек (какие записи должны наполняться динамическим содержимым) задаётся в качестве параметров наложения в конфигурационном каталоге cn=config, часть (критерии отбора и формирования самого динамического содержимого) — непосредственно в рабочем каталоге. Подробно был разобран алгоритм формирования динамического содержимого записей. Итоговые настройки каталога dc=mycompany,dc=ru выглядят так:
$ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config
Enter LDAP Password:
dn: olcDatabase={1}mdb,cn=config
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcSuffix: dc=mycompany,dc=ru
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to * by self write by * read
olcRootDN: cn=manager,ou=System,dc=mycompany,dc=ru
olcRootPW: {SSHA}PKFrwbIL/zLd3gabPPLxn1vNq2jQHj4g
olcDbIndex: objectClass eq
olcDbIndex: cn eq,sub,subinitial
olcDbDirectory: /var/lib/ldap/dc=mycompany,dc=ru
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: {0}dynlist
olcDlAttrSet: {0}groupOfURLs memberURL