Предпосылки: В организации, сведения о которой ведутся в нашем каталоге, два отдела: Developers и Designers. Информация о принадлежности пользователя отделу фиксируется как значение атрибута ou
в учётной записи каждого пользователя. На почтовом сервере организации обслуживаются почтовые ящики пользователей, адреса электронной почты фиксируются как значение атрибута mail
в учётной записи каждого пользователя.
Задача: Требуется создать списки рассылки, которые будут использоваться почтовым сервером для организации рассылки писем всем сотрудникам организации и отдельно сотрудникам каждого подразделения. Кроме того, для разграничения доступа различных приложений требуется организовать объекты POSIX-групп и классических групп для имеющихся отделов. Списки рассылки и групповые объекты должны постоянно отражать актуальную структуру организации.
В этом примере мы объединили три предыдущие задачи, чтобы продемонстрировать возможность настройки наложения dynlist
для поддержки в одном каталоге динамических объектов различных типов. Для разнообразия мы построим наши классические группы на другом объектном классе и с другим атрибутом, в который будет помещаться LDAP URI с параметрами вторичного поиска. Пользуясь тем, что настройки наложения dynlist
хранятся в индексированных (X-ORDERED
) атрибутах olcDLattrSet
, в этом примере мы немного больше внимания уделим работе с атрибутами такого типа.
Начинать, как всегда, будем с исходного положения каталога. Порядок работы будет такой: модификация записей пользователей, добавление записей динамических объектов, настройка наложения.
Сначала дополним учётные записи каталога перечисленными в предпосылках к задаче атрибутами с признаком принадлежности сотрудника к тому или иному отделу, а также адресом электронной почты:
dn: uid=antonova,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers
-
add: mail
mail: antonova@mycompany.ru
dn: uid=ivanov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers
-
add: mail
mail: ivanov@mycompany.ru
dn: uid=petrov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Developers
-
add: mail
mail: petrov@mycompany.ru
dn: uid=sidorov,ou=People,dc=mycompany,dc=ru
changetype: modify
add: ou
ou: Designers
-
add: mail
mail: sidorov@mycompany.ru
Применим изменения и посмотрим, в каком состоянии будут записи пользователей после модификации:
$ ldapmodify -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -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
Интересующие нас атрибуты на месте. Переходим к следующему этапу.
Наложение ещё не настроено, но ничто не мешает нам добавить записи, которые в дальнейшем станут динамическими объектами. По условиям задачи мы создадим списки рассылки в отдельном контейнере MailingLists, POSIX-группы в отдельном контейнере PosixGroups и классические группы в уже имеющемся контейнере Groups:
# Контейнер для списков рассылки
dn: ou=MailingLists,dc=mycompany,dc=ru
objectClass: organizationalUnit
ou: MailingLists
# Списки рассылки
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)
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))
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))
# Контейнер для POSIX-групп
dn: ou=PosixGroups,dc=mycompany,dc=ru
objectClass: organizationalUnit
ou: PosixGroups
# POSIX-группы отделов
dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Developers
gidNumber: 10001
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers))
dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru
objectClass: posixGroup
objectClass: labeledURIObject
cn: Designers
gidNumber: 10002
labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Designers))
# Группы отделов
dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Developers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Developers))
dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru
objectClass: organizationalRole
objectClass: extensibleObject
cn: Designers
memberURL: ldap:///ou=People,dc=mycompany,dc=ru??one?(&(objectClass=inetOrgPerson)(ou=Designers))
Ничего принципиально нового в динамических объектах этого примера нет. Списки рассылки, как и в первом примере, строятся на объектном классе groupOfURLs
, URI с параметрами вторичного запроса помещаются в атрибут memberURL
. POSIX-группы также полностью идентичны тем, что мы рассматривали во втором примере: строятся на объектном классе posixGroup
, а атрибут для хранения URI labeledURI
берётся из вспомогательного класса labeledURIObject
. А вот классические группы, как мы договаривались, на этот раз будут строиться на структурном объектном классе organizationalRole
. Атрибут членства этого класса roleOccupant
не входит в число его обязательных атрибутов, так что фиктивного члена для соблюдения требований схемы данных каталога заводить не нужно. Как и в предыдущем примере, у нашего класса среди обязательных или допустимых атрибутов нет подходящего для хранения LDAP URI, и опять для добавления такого атрибута в наши записи мы воспользовались специальным вспомогательным объектным классом extensibleObject, позволяющим включать в запись любой известный LDAP-серверу пользовательский атрибут. А раз уж мы можем включить любой атрибут, на этот раз мы решили воспользоваться атрибутом memberURL
, который также подходит для хранения LDAP URI, поскольку является производным от типа атрибута labeledURI
.
Стоп! А известен ли тип атрибута memberURL
нашему серверу slapd
? Мы же ещё не добавляли содержащий его описание (а также описание объектного класса groupOfURLs
) набор схемы данных dyngroup. Самое время сделать это:
$ ldapadd -x -D cn=config -W -f /etc/ldap/schema/dyngroup.ldif Enter LDAP Password: adding new entry "cn=dyngroup,cn=schema,cn=config"
Ну а теперь можно смело добавлять в каталоги будущие динамические объекты:
$ ldapadd -x -D cn=manager,ou=System,dc=mycompany,dc=ru -W -c -f ./003-add_dynamic_objects.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" adding new entry "ou=PosixGroups,dc=mycompany,dc=ru" adding new entry "cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru" adding new entry "cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru" adding new entry "cn=Developers,ou=Groups,dc=mycompany,dc=ru" adding new entry "cn=Designers,ou=Groups,dc=mycompany,dc=ru"
На данном этапе проверять их нет смысла, поскольку динамического содержимого у них всё равно не будет. Пора переходить к последнему этапу — настроить наложение dynlist
.
Начнём, как всегда, с загрузки динамического модуля наложения. Традиционный LDIF:
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: 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
Пора переходить к тому, ради чего и затевался этот пример. Принцип хранения настроек в виде строки, в которой параметры наложения разделяются пробельными символами, а также тот факт, что тип атрибута olcDLattrSet
является многозначным (то есть позволяет иметь несколько значений этого атрибута в одной записи), даёт администраторам возможность задавать настройки для построения различных динамических объектов в рамках одной записи конфигурационного каталога, описывающей наложение dynlist
. Кроме того, в определении типа атрибута olcDLattrSet
имеется ключевое слово X-ORDERED
, а значит значения этого атрибута индексируются и с ними можно работать как с упорядоченным множеством.
Разберёмся со всем этим на практике. Для начала добавим запись наложения к рабочей базе данных нашего каталога и уже при добавлении укажем настройки для двух различных динамических объектов:
dn: olcOverlay=dynlist,olcDatabase={1}mdb,cn=config
objectClass: olcDynamicList
olcOverlay: dynlist
olcDLattrSet: {0}groupOfURLs memberURL
olcDLattrSet: {1}organizationalRole memberURL roleOccupant
Как мы видим, в строке 4 указаны настройки для организации динамических списков рассылки электронной почты, а в строке 5 — настройки для организации классических динамических групп с объектным классом organizationalRole
и с атрибутом членства roleOccupant
. Смысл параметров настройки известен нам по предыдущим примерам. Поскольку мы только добавляем запись наложения, значения атрибута olcDLattrSet
можно было и не индексировать (то есть не указывать предваряющие эти значения числа в фигурных скобках), при добавлении они автоматически получили бы индекс в порядке их указания в LDIF-файле. Кроме того, порядок указания настроек наложения (то есть значений атрибута olcDLattrSet
) неважен — наложение в любом случае обработает все динамические объекты, соответствующие критериям, указанным в этих настройках.
Применим наш LDIF и проверим получившуюся запись наложения:
$ 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 olcDlAttrSet: {1}organizationalRole memberURL roleOccupant
Теперь проверим, как это повлияло на состояние динамических объектов в рабочем каталоге:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(objectClass=groupOfURLs)(objectClass=posixGroup)(objectClass=organizationalRole))' mail memberUid roleOccupant dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru
Всё правильно: списки рассылки и классические группы наполнились динамическим содержимым, а POSIX-группы — нет. Добавим настройки и для них:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDLattrSet
olcDLattrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
В строке 4 этого фрагмента LDIF добавляется новое значение атрибута olcDLattrSet
. Оно будет иметь индекс 1, то есть станет вторым среди значений этого атрибута и разместится между двумя уже имеющимися. Если бы мы не задавали явно индекс этому значению, оно было бы помещено в конец упорядоченного множества значений (стало бы третьим) и автоматически получило бы индекс 2. Опять же, на работу наложения порядок указания настроек динамических объектов не влияет.
Обратите внимание, что в данном случае мы указали промежуточный параметр настройки наложения (LDAP URI), ограничивающий область применения данных настроек (подробнее смотрите здесь). Также обратите внимание, что в качестве объектного класса, на который будет реагировать наложение dynlist
при отборе записей среди результатов операции поиска LDAP, мы указали не структурный класс posixGroup
(как мы поступали во втором примере), а вспомогательный класс labeledURIObject
. В данном случае допустимо было использовать любой из этих объектных классов, мы выбрали второй для разнообразия.
Применим наш LDIF и посмотрим получившиеся в результате настройки наложения:
$ ldapmodify -x -D cn=config -W -f ./103-dynlist_overlay_mod1.ldif Enter LDAP Password: modifying entry "olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config" $ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one -o ldif-wrap=no Enter LDAP Password: dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config objectClass: olcDynamicList olcOverlay: {0}dynlist olcDlAttrSet: {0}groupOfURLs memberURL olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid olcDlAttrSet: {2}organizationalRole memberURL roleOccupant
Всё в порядке. Посмотрим как дела с динамическими объектами в каталоге:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(|(objectClass=groupOfURLs)(objectClass=posixGroup)(objectClass=organizationalRole))' mail memberUid roleOccupant dn: cn=All,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru mail: ivanov@mycompany.ru mail: petrov@mycompany.ru dn: cn=Designers,ou=MailingLists,dc=mycompany,dc=ru mail: antonova@mycompany.ru mail: sidorov@mycompany.ru dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru memberUid: ivanov memberUid: petrov dn: cn=Designers,ou=PosixGroups,dc=mycompany,dc=ru memberUid: antonova memberUid: sidorov dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru dn: cn=Designers,ou=Groups,dc=mycompany,dc=ru roleOccupant: uid=antonova,ou=People,dc=mycompany,dc=ru roleOccupant: uid=sidorov,ou=People,dc=mycompany,dc=ru
Все наши объекты получили динамическое содержимое.
На этом можно было бы и завершить пример. Но что, если нам вдруг понадобилось что-то изменить в настройках наложения? Допустим, мы хотим добавить промежуточный параметр (ограничительный LDAP URI) к первой и последней настройке. Можно просто заменить все настройки разом:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcDLattrSet
olcDLattrSet: groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
olcDLattrSet: labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid
olcDLattrSet: organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant
А можно поступить хитрее, и, воспользовавшись тем, что у атрибута olcDLattrSet
упорядоченное множество значений, заменить только конкретные значения:
dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcDLattrSet
olcDLattrSet: {0}
-
add: olcDLattrSet
olcDLattrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL
-
delete: olcDLattrSet
olcDLattrSet: {2}
-
add: olcDLattrSet
olcDLattrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant
Какой из вариантов выбрать — решать Вам, мы же воспользуемся вторым:
$ ldapmodify -x -D cn=config -W -f ./104-dynlist_overlay_mod2.ldif Enter LDAP Password: modifying entry "olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config"
Посмотрим, что получилось в итоге с записью наложения dynlist
:
$ ldapsearch -xLLL -D cn=config -W -b olcDatabase={1}mdb,cn=config -s one -o ldif-wrap=no Enter LDAP Password: dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config objectClass: olcDynamicList olcOverlay: {0}dynlist olcDlAttrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid olcDlAttrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant
Всё как мы хотели. Поскольку наши динамические объекты и так находились в соответствующих подветках, на вывод содержимого рабочего каталога эти преобразования не повлияли.
Справедливости ради необходимо отметить, что внесение таких сложных изменений всё же повлияло на работу наложения dynlist
: в нашем тестовом каталоге (OpenLDAP версии 2.4.46) часть динамических объектов (списки рассылки) перестала наполняться динамическим содержимым. После перезапуска процесса slapd
всё встало на свои места. Что ж, динамическая конфигурация cn=config
всё ещё находится в стадии совершенствования.
В man-странице наложения dynlist сказано, что при использовании элемента управления LDAP ManageDsaIT (RFC 3296) наполнение динамического объекта динамическим содержимым не происходит, и в ответ на поисковый запрос с таким элементом управления возвращается исходная запись динамического объекта. Это может оказаться полезным, если мы хотим проверить, какие же данные на самом деле находятся в нашем каталоге.
К примеру, работая с каталогом, пользователь может извлечь POSIX-группу Developers:
$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru objectClass: posixGroup objectClass: labeledURIObject cn: Developers gidNumber: 10001 labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers)) memberUid: ivanov memberUid: petrov
Наличие замысловатого URI в атрибуте labeledURI
может навести его на мысль, что на самом деле это динамически наполняемый объект. Чтобы убедиться в этом, он должен выполнить такой же поисковый запрос, но с указанием элемента управления ManageDsaIT (для утилиты ldapsearch
он задаётся параметром -M
):
$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no -M dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru objectClass: posixGroup objectClass: labeledURIObject cn: Developers gidNumber: 10001 labeledURI: ldap:///ou=People,dc=mycompany,dc=ru?uid?one?(&(objectClass=inetOrgPerson)(ou=Developers))
В итоге из каталога извлекается исходная запись без динамического содержимого.
Ну хорошо, а можно ли не показывать пользователю тот самый замысловатый URI? Это несложно, нужно всего лишь ограничить доступ к атрибуту labeledURI
:
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to attrs=labeledURI
by * none
Поскольку это правило ACL имеет индекс 1, оно займёт место между двумя уже имеющимися правилами в настройках рабочей базы данных. Сам ACL предельно прост: мы запрещаем любой доступ к атрибуту labeledURI
всем пользователям (само собой, кроме пользователя, чьё DN указано в атрибуте olcRootDN
в настройках базы данных).
Добавим этот ACL и выведем список имеющихся:
$ ldapmodify -x -D cn=config -W -f ./105-add_labeleduri_acl.ldif Enter LDAP Password: modifying entry "olcDatabase={1}mdb,cn=config" $ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base olcAccess Enter LDAP Password: dn: olcDatabase={1}mdb,cn=config olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none olcAccess: {1}to attrs=labeledURI by * none olcAccess: {2}to * by self write by * read
А теперь вновь попробуем запросить POSIX-группу Developers:
$ ldapsearch -xLLL -b cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru -o ldif-wrap=no dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru objectClass: posixGroup objectClass: labeledURIObject cn: Developers gidNumber: 10001 memberUid: ivanov memberUid: petrov
Атрибут labeledURI
больше не отображается пользователю (в данном случае анонимному), поскольку у него нет на этот атрибут никаких прав. Но динамическое содержимое в записи появилось, а значит наложение dynlist
(которое также выполняется с правами текущего пользователя) сработало, следовательно, оно смогло прочитать значение атрибута labeledURI
записи cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru. То есть наше ACL работает только наполовину? На самом деле нет, всё дело в порядке применения ACL (который можно отследить, установив уровень журналирования slapd
в значение acl):
slapd
, согласно критериям поиска, переданным утилитой ldapsearch
, нашел требуемую запись, но не стал возвращать её утилите, а передал на обработку наложению dynlist
.dynlist
проанализировало полученную запись и обнаружило, что она отвечает критериям одного из динамических объектов (содержит класс labeledURIObject
и атрибут labeledURI
, а также находится в ветке ou=PosixGroups,dc=mycompany,dc=ru) и поэтому должна быть наполнена динамическим содержимым.dynlist
получило запись целиком (без ограничений), то оно считало параметры вторичного поиска из LDAP URI в значении атрибута labeledURI
и осуществило этот вторичный поиск (во время выполнения которого правила ACL применялись). По результатам поиска было сформировано динамическое содержимое, которым была дополнена исходная запись.ldapsearch
). Но перед возвратом slapd
применяет к ней правила ACL, так что динамическое содержимое в записи осталось, а атрибут labeledURI
— нет. Итоговая запись передаётся утилите ldapsearch
, которая отображает её пользователю.Так что никаких противоречий тут нет. Кстати, у добавленного нами правила ACL есть один побочный эффект: оно распространяется и на типы атрибутов, производные от labeledURI
. Убедимся в этом, получив динамический объект с другим атрибутом, в котором хранится LDAP URI:
$ ldapsearch -xLLL -b cn=Developers,ou=MailingLists,dc=mycompany,dc=ru -o ldif-wrap=no dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: Developers mail: ivanov@mycompany.ru mail: petrov@mycompany.ru
Как видите, атрибут memberURL
(производный от labeledURI
) также не отображается пользователю. Если же мы поступили бы наоборот, то есть в ACL ограничили доступ к атрибуту memberURL
, то на вышестоящий атрибут labeledURI
действие этого правила не распространялось бы. Такой побочный эффект от иерархии типов атрибутов всегда следует иметь ввиду при составлении ACL.
После того, как мы разобрали порядок применения ACL в OpenLDAP, становится очевидно, что ограничения можно наложить и на вывод самого динамического содержимого. Предположим, что мы хотим не показывать динамическое содержимое при анонимном подключении к каталогу (то есть показывать его только пользователям, прошедшим аутентификацию). Для этого можно составить такой ACL:
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {2}to attrs=mail,memberUid,roleOccupant
by users read
by * none
Поскольку это правило ACL имеет индекс 2, оно займёт третье место в списке ACL (перед последним в настоящий момент правилом). В строке 4 мы перечислили атрибуты, составляющие в нашем примере динамическое содержимое в динамических объектах. В строке 5 мы позволяем пользователям, прошедшим аутентификацию, считывать значения этих атрибутов, а в строке 6 запрещаем всем остальным доступ к ним. Таким образом, динамическое содержимое не будет отображаться при анонимном запросе в каталог.
Тут возникает небольшая проблема: атрибуты mail
содержатся не только в динамических списках рассылки электронной почты, но и во вполне статических записях пользователей в ветке ou=People,dc=mycompany,dc=ru. Чтобы ограничить доступ именно к динамическому содержимому, разобьём наше правило на три, ограничив доступ к конкретным атрибутам именно в тех ветках каталога, в которых содержатся динамические объекты:
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail
by users read
by * none
olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid
by users read
by * none
olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant
by users read
by * none
Наши правила ACL имеют индексы 2, 3 и 4, то есть они последовательно будут вставлены перед последним (самым общим) правилом в списке ACL. Применим изменения и проверим итоговые ACL:
$ ldapmodify -x -D cn=config -W -f ./106-add_dynamic_attrs_acl.ldif Enter LDAP Password: modifying entry "olcDatabase={1}mdb,cn=config" $ ldapsearch -xLLL -o ldif-wrap=no -D cn=config -W -b olcDatabase={1}mdb,cn=config -s base olcAccess Enter LDAP Password: dn: olcDatabase={1}mdb,cn=config olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none olcAccess: {1}to attrs=labeledURI by * none olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail by users read by * none olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid by users read by * none olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant by users read by * none olcAccess: {5}to * by self write by * read
Осталось проверить работу новых правил. Сначала запросим динамические объекты для отдела Developers анонимно:
$ ldapsearch -xLLL -b dc=mycompany,dc=ru '(cn=Developers)' dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: Developers dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru objectClass: posixGroup objectClass: labeledURIObject cn: Developers gidNumber: 10001 dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developers
Всё верно, динамическое содержимое не отображается. Теперь выполним этот же запрос от имени пользователя ivanov:
$ ldapsearch -xLLL -D uid=ivanov,ou=People,dc=mycompany,dc=ru -w ivanovPassword -b dc=mycompany,dc=ru '(cn=Developers)' dn: cn=Developers,ou=MailingLists,dc=mycompany,dc=ru objectClass: groupOfURLs cn: Developers mail: ivanov@mycompany.ru mail: petrov@mycompany.ru dn: cn=Developers,ou=PosixGroups,dc=mycompany,dc=ru objectClass: posixGroup objectClass: labeledURIObject cn: Developers gidNumber: 10001 memberUid: ivanov memberUid: petrov dn: cn=Developers,ou=Groups,dc=mycompany,dc=ru objectClass: organizationalRole objectClass: extensibleObject cn: Developers roleOccupant: uid=ivanov,ou=People,dc=mycompany,dc=ru roleOccupant: uid=petrov,ou=People,dc=mycompany,dc=ru
Пользователь, прошедший аутентификацию, получает доступ к динамическому содержимому записей. Наши ACL работают как ожидалось.
В ходе этого урока мы ознакомились в возможностью настройки наложения dynlist
для отбора и обработки сразу нескольких динамических объектов различных типов. Особое внимание было уделено приёмам работы с атрибутами X-ORDERED
, которые часто используются в объектах конфигурационного каталога cn=config
. Кроме того, была рассмотрена работа наложения dynlist
при запросах в каталог с указанием элемента управления ManageDsaIT, а также некоторые аспекты работы ACL OpenLDAP, связанные с построением и отображением динамического содержимого объектов. Итоговые настройки каталога dc=mycompany,dc=ru выглядят так (для удобства чтения ACL мы разбили их вывод на несколько строк):
$ 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 olcDbDirectory: /var/lib/ldap/dc=mycompany,dc=ru olcSuffix: dc=mycompany,dc=ru olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none olcAccess: {1}to attrs=labeledURI by * none olcAccess: {2}to dn.one="ou=MailingLists,dc=mycompany,dc=ru" attrs=mail by users read by * none olcAccess: {3}to dn.one="ou=PosixGroups,dc=mycompany,dc=ru" attrs=memberUid by users read by * none olcAccess: {4}to dn.one="ou=Groups,dc=mycompany,dc=ru" attrs=roleOccupant by users read by * none olcAccess: {5}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 dn: olcOverlay={0}dynlist,olcDatabase={1}mdb,cn=config objectClass: olcDynamicList olcOverlay: {0}dynlist olcDlAttrSet: {0}groupOfURLs ldap:///ou=MailingLists,dc=mycompany,dc=ru??one? memberURL olcDlAttrSet: {1}labeledURIObject ldap:///ou=PosixGroups,dc=mycompany,dc=ru??one? labeledURI memberUid:uid olcDlAttrSet: {2}organizationalRole ldap:///ou=Groups,dc=mycompany,dc=ru??one? memberURL roleOccupant