8. Контроль доступа


8.1. Введение

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

Доступ к Вашему каталогу может быть сконфигурирован двумя методами, первый из которых использует Конфигурационный файл slapd, а второй использует формат slapd-config(5) (описанный в разделе Настройка slapd).

Политика контроля доступа по умолчанию - разрешить доступ на чтение всем клиентам. Независимо от того, какая политика контроля доступа определена, для rootdn всегда разрешены полные права (то есть аутентификация, поиск, сравнение, чтение и запись) над чем угодно и где угодно в каталоге.

Как следствие, явное указание rootdn среди условий <by> бесполезно и, в добавок, приводит к снижению производительности.

В следующих подразделах даётся достаточно глубокое описание списков контроля доступа (Access Control List), за которым следует несколько примеров и рекомендаций. Полное описание смотрите в slapd.access(5).


8.2. Контроль доступа при использовании статической конфигурации

Доступ к записям и атрибутам назначается директивой access конфигурационного файла. Общая форма строки access следующая:

    <access directive> ::= access to <what>
        [by <who> [<access>] [<control>] ]+
    <what> ::= * |
        [dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
        [filter=<ldapfilter>] [attrs=<attrlist>]
    <basic-style> ::= regex | exact
    <scope-style> ::= base | one | subtree | children
    <attrlist> ::= <attr> [val[.<basic-style>]=<regex>] | <attr> , <attrlist>
    <attr> ::= <attrname> | entry | children
    <who> ::= * | [anonymous | users | self
            | dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
        [dnattr=<attrname>]
        [group[/<objectclass>[/<attrname>][.<basic-style>]]=<regex>]
        [peername[.<basic-style>]=<regex>]
        [sockname[.<basic-style>]=<regex>]
        [domain[.<basic-style>]=<regex>]
        [sockurl[.<basic-style>]=<regex>]
        [set=<setspec>]
        [aci=<attrname>]
    <access> ::= [self]{<level>|<priv>}
    <level> ::= none | disclose | auth | compare | search | read | write | manage
    <priv> ::= {=|+|-}{m|w|r|s|c|x|d|0}+
    <control> ::= [stop | continue | break]

где часть <what> определяет записи и/или атрибуты, к которым применяется доступ, часть <who> указывает, каким записям предоставляется доступ, и, наконец, часть <access> указывает предоставляемые полномочия доступа. Поддерживается указание нескольких триплетов <who> <access> <control>, что позволяет предоставить разным записям разный уровень доступа к одному и тому же набору записей и атрибутов. Здесь описаны не все опции контроля доступа, более детальное описание Вы найдёте в man-странице slapd.access(5).

8.2.1. Над чем осуществляется контроль доступа

Часть спецификации access <what> определяет записи и атрибуты, к которым применяется контроль доступа. Записи обычно выбираются двумя путями: по DN и по фильтру. В следующих критериях отбора описывается выбор записи по DN:

    to *
    to dn[.<basic-style>]=<regex>
    to dn.<scope-style>=<DN>

В первой форме выбираются все записи. Вторая форма может быть использована для выбора записей, соответствующих регулярному выражению, а не нормализованному DN целевой записи (в этом документе не даётся развёрнутого обсуждения использования второй формы). Третья форма используется для выбора записей, попадающих в запрашиваемый диапазон DN. <DN> - это строка, представляющая собой уникальное имя (Distinguished Name), как описано в RFC4514.

Диапазон может задаваться как base, one, subtree, или children. Здесь base соответствует только записи с указанным DN, one соответствует записям, для которых указанный DN является родительским, subtree соответствует всем записям поддерева, корнем которого является указанный DN, и children соответствует всем записям ниже указанного DN (но не самой записи с указанным DN).

Например, если в каталоге есть записи со следующими именами:

    0: o=suffix
    1: cn=Manager,o=suffix
    2: ou=people,o=suffix
    3: uid=kdz,ou=people,o=suffix
    4: cn=addresses,uid=kdz,ou=people,o=suffix
    5: uid=hyc,ou=people,o=suffix

то:

Записи также могут выбираться с использованием фильтра:

    to filter=<ldap filter>

где <ldap filter> - это строка, представляющая собою поисковый фильтр LDAP, как описано в RFC4515. Например:

    to filter=(objectClass=person)

Обратите внимание, что записи могут выбираться сразу и по DN и по фильтру путём включения обоих критериев отбора в условие <what>.

    to dn.one="ou=people,o=suffix" filter=(objectClass=person)

Атрибуты в составе записи указываются путём включения разделённого запятыми списка имён атрибутов в условие <what>:

    attrs=<attribute list>

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

    attrs=<attribute> val[.<style>]=<regex>

Есть два специальных псевдо-атрибута entry и children. Чтобы прочитать (и, следовательно, вернуть) целевую запись, субъект должен иметь права read на атрибут entry целевой записи. Чтобы осуществить поиск, субъект должен иметь права search на атрибут entry записи - базы поиска. Чтобы добавить или удалить запись, субъект должен иметь права write на атрибут entry этой записи, А ТАКЖЕ должен иметь права write на атрибут children родительской записи. Чтобы переименовать запись, субъект должен иметь права write на атрибут entry этой записи, А ТАКЖЕ иметь права write на атрибуты children как старой, так и новой родительских записей. В конце подраздела будут даны примеры, которые помогут Вам прояснить ситуацию.

И наконец, есть специальный критерий отбора записей "*", который используется для выбора любой записи. Он используется, когда в условии <what> не было указано других критериев отбора. Это эквивалент "dn=.*"

8.2.2. Кому даются права на доступ

Часть спецификации access <who> определяет сущность или сущности, которым предоставляются права на доступ. Обратите внимание, что права предоставляются "сущностям", а не "записям". В следующей таблице приведены синтаксисы спецификации сущностей:

Таблица 8.1: Синтаксисы спецификации сущностей доступа
Синтаксис спецификацииОписание сущности
*Все, включая анонимных пользователей и пользователей, прошедших аутентификацию
anonymousАнонимные (не прошедшие аутентификацию) пользователи
usersПользователи, прошедшие аутентификацию
selfПользователь, ассоциированный с целевой записью
dn[.<basic-style>]=<regex>Пользователи, записи которых соответствуют регулярному выражению
dn.<scope-style>=<DN>Пользователи в диапазоне DN

Синтаксисы спецификации DN подчиняются тем же правилам, что и синтаксисы спецификации DN в условии <what>.

Также поддерживаются другие факторы контроля. Например, сущности в условии <who> могут назначаться как записи, перечисленные в атрибуте, значением которого являются DN, той записи, к которой применяется правило доступа:

    dnattr=<имя атрибута, значением которого является DN>

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

Некоторые факторы неуместно использовать в определённых (или даже во всех) окружениях. Например, фактор домена опирается на IP-адреса для разрешения доменных имён. Поскольку они легко подменяются, следует избегать использования фактора домена.

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

В условии <access>может указываться один из следующих типов прав доступа:

Таблица 8.2: Уровни доступа
УровеньПривилегииОписание
none =0нет доступа
disclose =dтребуется для выдачи информации в случае ошибки
auth =dxтребуется для аутентификации (подключения)
compare =cdxтребуется для сравнения
search =scdxтребуется для применения поисковых фильтров
read =rscdxтребуется для чтения результатов поиска
write =wrscdxтребуется для изменения/переименования
manage =mwrscdxтребуется для осуществления управления

Каждый уровень включает в себя все более низкие уровни доступа. Таким образом, например, предоставление кому-нибудь доступа к какой-либо записи уровня write даёт ему также привилегии доступа read, search, compare, auth и disclose. Однако, их можно задать и явно для предоставления каких-нибудь специальных разрешений.

8.2.4. Принятие решения о предоставлении доступа

При принятии решения о предоставлении кому-либо, совершающему запрос, доступа к той или иной записи и/или атрибуту, slapd сравнивает запрашиваемые запись и/или атрибут с критериями отбора <what>, заданными в конфигурационном файле. Для каждой записи сначала применяются директивы контроля доступа, относящиеся к той базе данных, в которой эта запись содержится (или глобальные директивы, если эта запись не содержится ни в одной из баз данных), а затем применяются глобальные директивы контроля доступа. Существует некоторая тонкость при применении списков контроля доступа. Глобальные списки контроля доступа добавляются к спискам, определённым для каждой базы данных, и, если после такого сложения результирующий список оказывается непустым, тогда в его конец добавляется неявная директива access to * by * none. Если же результирующий список пуст, то есть для какого-либо механизма манипуляции данными не задано директив, то по умолчанию предоставляется доступ на чтение.

Следуя этому приоритету, директивы access проверяются в порядке их указания в конфигурационном файле. Slapd останавливает проверку на первом критерии отбора <what>, который соответствует целевой записи и/или атрибуту. Директива access с этим критерием отбора и будет той, по которой slapd будет принимать решение о предоставлении доступа.

Затем slapd сравнивает сущность, запрашивающую доступ, с критериями отбора <who> выбранной выше директивы access в порядке их указания. Он останавливает сравнение на первом критерии отбора <who>, который соответствует целевой сущности. Так определяется, будет ли сущность, запрашивающая доступ, реально иметь доступ к записи и/или атрибуту.

Наконец, slapd сравнивает уровень доступа, предоставленный в выбранном условии <access>, с уровнем доступа, который запрашивает клиент. Если в условии разрешён больший или равный уровень доступа, то клиенту предоставляется доступ. В противном случае доступ запрещается.

Порядок оценки директив access делает важным порядок их размещения в конфигурационном файле. Если одна директива access выдвигает более конкретные условия отбора записей, чем другая, она должна быть указана раньше в конфигурационном файле. Аналогично, если один критерий отбора <who> выдвигает более конкретные условия отбора, нежели другой, он должен указываться раньше в директиве access. Приведённые ниже примеры настройки контроля доступа помогут прояснить ситуацию.

8.2.5. Примеры настройки контроля доступа

Инструмент контроля доступа, описанный выше, довольно мощный. Чтобы продемонстрировать это, приведём несколько примеров его использования.

Простой пример:

    access to * by * read

Эта директива access предоставляет доступ на чтение всем.

    access to *
        by self write
        by anonymous auth
        by * read

Эта директива позволяет пользователю изменять свою запись, позволяет анонимным пользователям производить аутентификацию по этим записям, наконец, позволяет всем остальным читать эти записи. Обратите внимание, что применяется только первое совпавшее условие by <who>. Поэтому анонимным пользователям предоставляются только права auth, а не read. Последнее же условие могло было быть "by users read".

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

    access to *
        by ssf=128 self write
        by ssf=64 anonymous auth
        by ssf=64 users read

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

В следующем примере показано использование указателей стиля диапазона для выбора записей по DN. Приведены две директивы, для которых важен порядок их применения:

    access to dn.children="dc=example,dc=com"
         by * search
    access to dn.children="dc=com"
         by * read

Доступ на чтение предоставляется к дочерним записям поддерева dc=com, за исключением тех записей (являющихся дочерними для поддерева dc=example,dc=com), на которые предоставлены права на поиск. На саму запись dc=com не предоставляется никаких прав, поскольку нет директив, условия <what> которых совпадают с этим DN. Если поменять порядок этих директив access, то последняя из них никогда не будет применяться, поскольку все записи, дочерние для dc=example,dc=com также являются дочерними и для dc=com.

Также обратите внимание, что если не было совпадений ни с одной из директив access to или ни с одним из условий by <who>, доступ будет запрещён. То есть, каждая директива access to заканчивается неявным условием by * none. Глобальные списки контроля доступа добавляются к спискам, определённым для каждой базы данных, и, если после такого сложения результирующий список оказывается непустым, тогда в его конец добавляется неявная директива access to * by * none. Если же результирующий список пуст, то есть для какого-либо механизма манипуляции данными не задано директив, то по умолчанию предоставляется доступ на чтение.

В следующем примере снова показана важность порядка указания как директив access, так и условий by <who>. В нём также показано использование критерия отбора атрибутов для предоставления доступа к указанному атрибуту, а также различные критерии отбора в условиях <who>.

    access to dn.subtree="dc=example,dc=com" attrs=homePhone
        by self write
        by dn.children="dc=example,dc=com" search
        by peername.regex=IP=10\..+ read
    access to dn.subtree="dc=example,dc=com"
        by self write
        by dn.children="dc=example,dc=com" search
        by anonymous auth

В этом примере контроль доступа осуществляется над записями поддерева "dc=example,dc=com". Ко всем атрибутам, за исключением homePhone, у пользователя есть право изменять свою запись, у записей, дочерних к example.com есть право производить поиск по этим записям, больше прав никому не даётся (неявное условие by * none), за исключением аутентификации/авторизации (которые всегда производятся анонимным пользователем). Атрибут homePhone доступен для изменения самой записью, для поиска - записям, дочерним к example.com, для чтения - клиентам, подключающимся из сети 10, в остальных случаях доступа нет (неявное условие by * none). Кроме того, остальные попытки доступа запрещены неявной директивой access to * by * none.

Иногда бывает полезно разрешить некоторым DN добавлять или удалять самих себя из атрибута. Например, если Вам нужно создать группу и разрешить людям добавлять или удалять только свои собственные DN из атрибута member, Вы можете сделать это с помощью такой директивы access:

    access to attrs=member,entry
         by dnattr=member selfwrite

Критерий отбора dnattr в условии <who> говорит о том, что доступ осуществляется к записям, перечисленным в атрибуте member. Уровень доступа selfwrite говорит о том, что записи, перечисленные в атрибуте member, могут добавлять или удалять только свои собственные DN из этого атрибута, и ни какие другие значения. Добавление атрибута entry необходимо, поскольку доступ к записи требуется для доступа к любому атрибуту этой записи.


8.3. Контроль доступа при использовании динамической конфигурации

Доступ к записям и атрибутам slapd контролируется атрибутом olcAccess, значения которого - это последовательность директив access. Общая форма конфигурации olcAccess следующая:

    olcAccess: <access directive>
    <access directive> ::= to <what>
        [by <who> [<access>] [<control>] ]+
    <what> ::= * |
        [dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
        [filter=<ldapfilter>] [attrs=<attrlist>]
    <basic-style> ::= regex | exact
    <scope-style> ::= base | one | subtree | children
    <attrlist> ::= <attr> [val[.<basic-style>]=<regex>] | <attr> , <attrlist>
    <attr> ::= <attrname> | entry | children
    <who> ::= * | [anonymous | users | self
            | dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
        [dnattr=<attrname>]
        [group[/<objectclass>[/<attrname>][.<basic-style>]]=<regex>]
        [peername[.<basic-style>]=<regex>]
        [sockname[.<basic-style>]=<regex>]
        [domain[.<basic-style>]=<regex>]
        [sockurl[.<basic-style>]=<regex>]
        [set=<setspec>]
        [aci=<attrname>]
    <access> ::= [self]{<level>|<priv>}
    <level> ::= none | disclose | auth | compare | search | read | write | manage
    <priv> ::= {=|+|-}{m|w|r|s|c|x|d|0}+
    <control> ::= [stop | continue | break]

где часть <what> определяет записи и/или атрибуты, к которым применяется доступ, часть <who> указывает, каким записям предоставляется доступ, и, наконец, часть <access> указывает предоставляемые полномочия доступа. Поддерживается указание нескольких триплетов <who> <access> <control>, что позволяет предоставить разным записям разный уровень доступа к одному и тому же набору записей и атрибутов. Здесь описаны не все опции контроля доступа, более детальное описание Вы найдёте в man-странице slapd.access(5).

8.3.1. Над чем осуществляется контроль доступа

Часть спецификации access <what> определяет записи и атрибуты, к которым применяется контроль доступа. Записи обычно выбираются двумя путями: по DN и по фильтру. В следующих критериях отбора описывается выбор записи по DN:

    to *
    to dn[.<basic-style>]=<regex>
    to dn.<scope-style>=<DN>

В первой форме выбираются все записи. Вторая форма может быть использована для выбора записей, соответствующих регулярному выражению а не нормализованному DN целевой записи (в этом документе не даётся развёрнутого обсуждения использования второй формы). Третья форма используется для выбора записей, попадающих в запрашиваемый диапазон DN. <DN> - это строка, представляющая собой уникальное имя (Distinguished Name), как описано в RFC4514.

Диапазон может задаваться как base, one, subtree, или children. Здесь base соответствует только записи с указанным DN, one соответствует записям, для которых указанный DN является родительским, subtree соответствует всем записям поддерева, корнем которого является указанный DN, и children соответствует всем записям ниже указанного DN (но не самой записи с указанным DN).

Например, если в каталоге есть записи со следующими именами:

    0: o=suffix
    1: cn=Manager,o=suffix
    2: ou=people,o=suffix
    3: uid=kdz,ou=people,o=suffix
    4: cn=addresses,uid=kdz,ou=people,o=suffix
    5: uid=hyc,ou=people,o=suffix

то:

Записи также могут выбираться с использованием фильтра:

    to filter=<ldap filter>

где <ldap filter> - это строка, представляющая собою поисковый фильтр LDAP, как описано в RFC4515. Например:

    to filter=(objectClass=person)

Обратите внимание, что записи могут выбираться сразу и по DN, и по фильтру путём включения обоих критериев отбора в условие <what>.

    to dn.one="ou=people,o=suffix" filter=(objectClass=person)

Атрибуты в составе записи указываются путём включения разделённого запятыми списка имён атрибутов в условие <what>:

    attrs=<attribute list>

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

    attrs=<attribute> val[.<style>]=<regex>

Есть два специальных псевдо-атрибута entry и children. Чтобы прочитать (и, следовательно, вернуть) целевую запись, субъект должен иметь права read на атрибут entry целевой записи. Чтобы осуществить поиск, субъект должен иметь права search на атрибут entry записи - базы поиска. Чтобы добавить или удалить запись, субъект должен иметь права write на атрибут entry этой записи, А ТАКЖЕ должен иметь права write на атрибут children родительской записи. Чтобы переименовать запись, субъект должен иметь права write на атрибут entry этой записи, А ТАКЖЕ иметь права write на атрибуты children как старой, так и новой родительских записей. В конце подраздела будут даны примеры, которые помогут Вам прояснить ситуацию.

И наконец, есть специальный критерий отбора записей "*", который используется для выбора любой записи. Он используется, когда в условии <what> не было указано других критериев отбора. Это эквивалент "dn=.*"

8.3.2. Кому даются права на доступ

Часть спецификации access <who> определяет сущность или сущности, которым предоставляются права на доступ. Обратите внимание, что права предоставляются "сущностям", а не "записям". В следующей таблице приведены синтаксисы спецификации сущностей:

Table 8.3: Синтаксисы спецификации сущностей доступа
Синтаксис спецификацииОписание сущности
*Все, включая анонимных пользователей и пользователей, прошедших аутентификацию
anonymousАнонимные (не прошедшие аутентификацию) пользователи
usersПользователи, прошедшие аутентификацию
selfПользователь, ассоциированный с целевой записью
dn[.<basic-style>]=<regex>Пользователи, записи которых соответствуют регулярному выражению
dn.<scope-style>=<DN>Пользователи в диапазоне DN

Синтаксисы спецификации DN подчиняются тем же правилам, что и синтаксисы спецификации DN в условии <what>.

Также поддерживаются другие факторы контроля. Например, сущности в условии <who> могут назначаться как записи, перечисленные в атрибуте, значением которого являются DN, той записи, к которой применяется правило доступа:

    dnattr=<имя атрибута, значением которого является DN>

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

Некоторые факторы неуместно использовать в определённых (или даже во всех) окружениях. Например, фактор домена опирается на IP-адреса для разрешения доменных имён. Поскольку они легко подменяются, следует избегать использования фактора домена.

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

В условии <access>может указываться один из следующих типов прав доступа:

Table 8.4: Уровни доступа
УровеньПривилегииОписание
none=0нет доступа
disclose=dтребуется для выдачи информации в случае ошибки
auth=dxтребуется для аутентификации (подключения)
compare=cdxтребуется для сравнения
search=scdxтребуется для применения поисковых фильтров
read=rscdxтребуется для чтения результатов поиска
write=wrscdxтребуется для изменения/переименования
manage=mwrscdxтребуется для осуществления управления

Каждый уровень включает в себя все более низкие уровни доступа. Таким образом, например, предоставление кому-нибудь доступа к какой-либо записи уровня write даёт ему также привилегии доступа read, search, compare, auth и disclose. Однако, их можно задать и явно для предоставления каких-нибудь специальных разрешений.

8.3.4. Принятие решения о предоставлении доступа

При принятии решения о предоставлении кому-либо, совершающему запрос, доступа к той или иной записи и/или атрибуту, slapd сравнивает запрашиваемые запись и/или атрибут с критериями отбора <what>, заданными при конфигурации. Для каждой записи сначала применяются директивы контроля доступа, относящиеся к той базе данных, в которой эта запись содержится (или глобальные директивы, если эта запись не содержится ни в одной из баз данных), а затем применяются глобальные директивы контроля доступа (которые содержатся в определении базы данных frontend). Существует некоторая тонкость при применении списков контроля доступа. Глобальные списки контроля доступа добавляются к спискам, определённым для каждой базы данных, и, если после такого сложения результирующий список оказывается непустым, тогда в его конец добавляется неявная директива access to * by * none. Если же результирующий список пуст, то есть для какого-либо механизма манипуляции данными не задано директив, то по умолчанию предоставляется доступ на чтение.

Следуя этому приоритету, директивы access проверяются в порядке их указания в конфигурационном атрибуте. Slapd останавливает проверку на первом критерии отбора <what>, который соответствует целевой записи и/или атрибуту. Директива access с этим критерием отбора и будет той, по которой slapd будет принимать решение о предоставлении доступа.

Затем slapd сравнивает сущность, запрашивающую доступ, с критериями отбора <who> выбранной выше директивы access в порядке их указания. Он останавливает сравнение на первом критерии отбора <who>, который соответствует целевой сущности. Так определяется, будет ли сущность, запрашивающая доступ, реально иметь доступ к записи и/или атрибуту.

Наконец, slapd сравнивает уровень доступа, предоставленный в выбранном условии <access>, с уровнем доступа, который запрашивает клиент. Если в условии разрешён больший или равный уровень доступа, то клиенту предоставляется доступ. В противном случае доступ запрещается.

Порядок оценки директив access делает важным порядок их размещения в конфигурационном файле. Если одна директива access выдвигает более конкретные условия отбора записей, чем другая, она должна быть указана раньше при конфигурации. Аналогично, если один критерий отбора <who> выдвигает более конкретные условия отбора, нежели другой, он должен указываться раньше в директиве access. Приведённые ниже примеры настройки контроля доступа помогут прояснить ситуацию.

8.3.5. Примеры настройки контроля доступа

Инструмент контроля доступа, описанный выше, довольно мощен. Чтобы продемонстрировать это, приведём несколько примеров его использования.

Простой пример:

    olcAccess: to * by * read

Эта директива access предоставляет доступ на чтение всем.

    olcAccess: to *
        by self write
        by anonymous auth
        by * read

Эта директива позволяет пользователю изменять свою запись, позволяет анонимным пользователям производить аутентификацию по этим записям, наконец, позволяет всем остальным читать эти записи. Обратите внимание, что применяется только первое совпавшее условие by <who>. Поэтому анонимным пользователям предоставляются только права auth, а не read. Последнее же условие могло было быть "by users read".

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

    olcAccess: to *
        by ssf=128 self write
        by ssf=64 anonymous auth
        by ssf=64 users read

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

В следующем примере показано использование указателей стиля диапазона для выбора записей по DN. Приведены две директивы, для которых важен порядок их применения:

    olcAccess: to dn.children="dc=example,dc=com"
         by * search
    olcAccess: to dn.children="dc=com"
         by * read

Доступ на чтение предоставляется к дочерним записям поддерева dc=com, за исключением тех записей (являющихся дочерними для поддерева dc=example,dc=com), на которые предоставлены права на поиск. На саму запись dc=com не предоставляется никаких прав, поскольку нет директив, условия <what> которых совпадают с этим DN. Если поменять порядок этих директив access, то последняя из них никогда не будет применяться, поскольку все записи, дочерние для dc=example,dc=com также являются дочерними и для dc=com.

Также обратите внимание, что если не было совпадений ни с одной из директив olcAccess: to или ни с одним из условий by <who>, доступ будет запрещён. Глобальные списки контроля доступа добавляются к спискам, определённым для каждой базы данных, и, если после такого сложения результирующий список оказывается непустым, тогда в его конец добавляется неявная директива access to * by * none. Если же результирующий список пуст, то есть для какого-либо механизма манипуляции данными не задано директив, то по умолчанию предоставляется доступ на чтение.

В следующем примере снова показана важность порядка указания как директив access, так и условий by <who>. В нём также показано использование критерия отбора атрибутов для предоставления доступа к указанному атрибуту, а также различные критерии отбора в условиях <who>.

    olcAccess: to dn.subtree="dc=example,dc=com" attrs=homePhone
        by self write
        by dn.children=dc=example,dc=com" search
        by peername.regex=IP=10\..+ read
    olcAccess: to dn.subtree="dc=example,dc=com"
        by self write
        by dn.children="dc=example,dc=com" search
        by anonymous auth

В этом примере контроль доступа осуществляется над записями поддерева "dc=example,dc=com". Ко всем атрибутам, за исключением homePhone, у пользователя есть право изменять свою запись, у записей, дочерних к example.com есть право производить поиск по этим записям, больше прав никому не даётся (неявное условие by * none), за исключением аутентификации/авторизации (которые всегда производятся анонимным пользователем). Атрибут homePhone доступен для изменения самой записью, для поиска - записям, дочерним к example.com, для чтения - клиентам, подключающимся из сети 10, в остальных случаях доступа нет (неявное условие by * none). Кроме того, остальные попытки доступа запрещены неявной директивой access to * by * none.

Иногда бывает полезно разрешить некоторым DN добавлять или удалять самих себя из атрибута. Например, если Вам нужно создать группу и разрешить людям добавлять или удалять только свои собственные DN из атрибута member, Вы можете сделать это с помощью такой директивы access:

    olcAccess: to attrs=member,entry
         by dnattr=member selfwrite

Критерий отбора dnattr в условии <who> говорит о том, что доступ осуществляется к записям, перечисленным в атрибуте member. Уровень доступа selfwrite говорит о том, что записи, перечисленные в атрибуте member, могут добавлять или удалять только свои собственные DN из этого атрибута, и ни какие другие значения. Добавление атрибута entry необходимо, поскольку доступ к записи требуется для доступа к любому атрибуту этой записи.

8.3.6. Порядок применения контроля доступа

Поскольку порядок директив olcAccess имеет важное значение для принятия по ним правильного решения, а атрибуты LDAP по умолчанию не заботятся о сохранении порядка своих значений, OpenLDAP использует обычное расширение схемы данных для поддержания фиксированного порядка этих значений. Упорядочение устанавливается добавлением числового индекса "{X}" перед каждым значением атрибута, аналогично тому, как упорядочиваются записи конфигурации. Эти метки индексов автоматически создаются slapd, их не надо указывать при первоначальном определении значений. Например, когда Вы создаёте настройки

    olcAccess: to attrs=member,entry
         by dnattr=member selfwrite
    olcAccess: to dn.children="dc=example,dc=com"
         by * search
    olcAccess: to dn.children="dc=com"
         by * read

, то при их извлечении с помощью slapcat или ldapsearch, в них будет содержаться:

    olcAccess: {0}to attrs=member,entry
         by dnattr=member selfwrite
    olcAccess: {1}to dn.children="dc=example,dc=com"
         by * search
    olcAccess: {2}to dn.children="dc=com"
         by * read

Числовой индекс может быть использован для указания того, какое конкретно значение Вы хотите изменить при редактировании правил контроля доступа с помощью утилиты ldapmodify. Этот индекс может быть использован вместо (или в дополнение к) фактическому значению атрибута olcAccess. Использование числового индекса может очень помочь, если требуется управлять сразу несколькими правилами контроля доступа.

Например, Вам нужно изменить второе правило из приведённого выше примера, чтобы предоставить доступ на запись вместо доступа на выполнение поиска. Можно попробовать использовать такой LDIF:

    changetype: modify
    delete: olcAccess
    olcAccess: to dn.children="dc=example,dc=com" by * search
    -
    add: olcAccess
    olcAccess: to dn.children="dc=example,dc=com" by * write
    -

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

    changetype: modify
    delete: olcAccess
    olcAccess: {1}
    -
    add: olcAccess
    olcAccess: {1}to dn.children="dc=example,dc=com" by * write
    -

В этом примере удаляется любое правило, содержащееся в значении номер 1 атрибута olcAccess (независимо от содержимого этого значения) и добавляется новое значение, которое явно вставлено как значение номер 1. В результате получаем:

    olcAccess: {0}to attrs=member,entry
         by dnattr=member selfwrite
    olcAccess: {1}to dn.children="dc=example,dc=com"
         by * write
    olcAccess: {2}to dn.children="dc=com"
         by * read

, а это именно то, что было задумано.


8.4. Типичные примеры контроля доступа

8.4.1. Основные ACL

В общем случае нужно начать с некоторых основных ACL, таких как:

    access to attrs=userPassword
        by self =xw
        by anonymous auth
        by * none


      access to *
        by self write
        by users read
        by * none

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

Второй ACL позволяет пользователям полный доступ к своим записям, пользователям, прошедшим аутентификацию, - доступ на чтение ко всему, и (неявно) запрещает любой доступ всем остальным (в данном случае, анонимным пользователям).

8.4.2. Соответствие анонимным пользователям и пользователям, прошедшим аутентификацию

Анонимный пользователь имеет пустой DN. Хотя для обозначения анонимного пользователя можно использовать конструкции dn.exact="" или dn.regex="^$", slapd(8) предлагает использовать вместо них термин anonymous.

    access to *
      by anonymous none
      by * read

Здесь полностью запрещён доступ анонимным пользователям, остальным пользователям предоставляются права на чтение.

Пользователи, прошедшие аутентификацию, имеют предметные DN. Хотя для обозначения любого пользователя, прошедшего аутентификацию, можно использовать конструкцию dn.regex=".+", OpenLDAP предлагает использовать вместо неё термин users.

    access to *
      by users read
      by * none

Этот ACL предоставляет права на чтение пользователям, прошедшим аутентификацию, и запрещает доступ всем остальным (то есть анонимным пользователям).

8.4.3. Контроль доступа для rootdn

Вы можете задать rootdn в slapd.conf(5) или в slapd.d без указания rootpw. Затем Вы должны добавить актуальную запись каталога с тем же самым DN, например:

    dn: cn=Manager,o=MyOrganization
    cn: Manager
    sn: Manager
    objectClass: person
    objectClass: top
    userPassword: {SSHA}someSSHAdata

После этого для подключения под учётной записью rootdn будет необходимо произвести стандартную процедуру подключения для заданного DN, которая в свою очередь, требует прав доступа auth к записи с этим DN и атрибуту userPassword, и эти права можно ограничить с помощью ACL. Например:

    access to dn.base="cn=Manager,o=MyOrganization"
      by peername.regex=127\.0\.0\.1 auth
      by peername.regex=192\.168\.0\..* auth
      by users none
      by * none

ACL, приведённые выше, разрешают подключение под учётной записью rootdn только с localhost и из сети 192.168.0.0/24.

8.4.4. Управление доступом с помощью групп

Это можно сделать несколькими способами. Покажем здесь один из них. Рассмотрим следующий макет DIT:

    +-dc=example,dc=com
    +---cn=administrators,dc=example,dc=com
    +---cn=fred blogs,dc=example,dc=com

и следующий объект группы (в формате LDIF):

    dn: cn=administrators,dc=example,dc=com
    cn: administrators of this region
    objectclass: groupOfNames  (важно для применения группового acl)
    member: cn=fred blogs,dc=example,dc=com
    member: cn=somebody else,dc=example,dc=com

Теперь можно предоставить доступ членам этой группы, добавив соответствующие условия by group в директиве access в slapd.conf(5). Например:

    access to dn.children="dc=example,dc=com"
        by self write
        by group.exact="cn=Administrators,dc=example,dc=com" write
        by * auth

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

    access to dn.regex="(.+,)?ou=People,(dc=[^,]+,dc=[^,]+)$"
             attrs=children,entry,uid
        by group.expand="cn=Managers,$2" write
        by users read
        by * auth

В нашем примере предполагается, что члены группы находятся в атрибуте типа member объектного класса groupOfNames. Если Вам нужно использовать для группы другой объектный класс и/или другой тип атрибута, то воспользуйтесь следующим (сокращённым) синтаксисом в slapd.conf(5):

    access to <what>
            by group/<объектный класс>/<имя атрибута>=<DN> <access>

Например:

    access to *
      by group/organizationalRole/roleOccupant="cn=Administrator,dc=example,dc=com" write

В этом случае используется объектный класс organizationalRole, содержащий DN администратора в атрибуте roleOccupant. Например:

    dn: cn=Administrator,dc=example,dc=com
    cn: Administrator
    objectclass: organizationalRole
    roleOccupant: cn=Jane Doe,dc=example,dc=com


Примечание: Указанный для членства в группе тип атрибута ДОЛЖЕН ИМЕТЬ синтаксис DN или NameAndOptionalUID, а указанный объектный класс ДОЛЖЕН ПОЗВОЛЯТЬ использование этого типа атрибута.

При контроле доступа также могут применяться динамические группы. Узнать о них можно в slapo-dynlist(5) и в подразделе о наложении Динамические списки.

8.4.5. Предоставление доступа к подмножеству атрибутов

Вы можете предоставить доступ к набору атрибутов, указав список имен атрибутов в условии ACL to. Чтобы это работало, Вам необходимо также предоставить доступ к самой записи с помощью псевдо-атрибута entry. Также обратите внимание на то, как псевдо-атрибут children позволяет управлять возможностью добавлять, удалять и переименовывать записи.

    # mail: пользователь, ассоциированный с самой записью, имеет право на изменение,
    # пользователи, прошедшие аутентификацию, имеют право на чтение
    access to attrs=mail
      by self write
      by users read
      by * none

    # cn, sn: пользователь, ассоциированный с самой записью, имеет право на изменение,
    # все остальные имеют право на чтение
    access to attrs=cn,sn
      by self write
      by * read

    # непосредственные подзаписи (children): только пользователь, ассоциированный с самой записью,
    # может добавлять/удалять записи, непосредственно дочерние данной записи
    access to attrs=children
      by self write

    # сама запись (entry): пользователь, ассоциированный с самой записью, имеет право на изменение,
    # все остальные имеют право на чтение
    access to attrs=entry
      by self write
      by * read

    # другие атрибуты: пользователь, ассоциированный с самой записью, имеет право на изменение,
    # всем остальным доступ запрещён
    access to *
      by self write
      by * none

В списке атрибутов можно также указать имена объектных классов, в этом случае права доступа назначаются всем атрибутам, которые требуются и/или разрешены данным объектным классом. Имена в списке attrlist, начинающиеся с @, сразу рассматриваются как имена объектных классов. Если же имя начинается с !, оно также рассматривается как имя объектного класса, но в этом случае права доступа назначаются всем атрибутам, которые не требуются и не разрешены данным объектным классом.

8.4.6. Разрешение пользователю изменять все записи, находящиеся ниже его собственной

Дадим права пользователю изменять свою собственную запись и все записи-потомки:

    access to dn.regex="(.+,)?(uid=[^,]+,o=Company)$"
       by dn.exact,expand="$2" write
       by anonymous auth

(Позже сюда будут добавлены дополнительные примеры).

8.4.7. Разрешение на создание записи

Предположим, у Вас есть такая структура:

        o=<basedn>
            ou=domains
                associatedDomain=<somedomain>
                    ou=users
                        uid=<someuserid>
                        uid=<someotheruserid>
                    ou=addressbooks
                        uid=<someuserid>
                            cn=<someone>
                            cn=<someoneelse>

и, для другого домена <someotherdomain>:

        o=<basedn>
            ou=domains
                associatedDomain=<someotherdomain>
                    ou=users
                        uid=<someuserid>
                        uid=<someotheruserid>
                    ou=addressbooks
                        uid=<someotheruserid>
                            cn=<someone>
                            cn=<someoneelse>

Теперь, если Вам нужно позволить пользователю uid=<someuserid> создавать записи ТОЛЬКО в пределах своей собственной области, можно написать примерно такой ACL:

    # данное правило позволяет пользователям из "associatedDomain=<совпавший домен>"
    # вносить изменения ниже "ou=addressbook,associatedDomain=<совпавший домен>,ou=domains,o=<basedn>",
    # то есть пользователь может изменять ЛЮБУЮ запись в адресной книге своего домена;
    # это разрешение необходимое, но не достаточное, следующее
    # разрешение дополнительно ограничит данное разрешение


    access to dn.regex="^ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$" attrs=children
            by dn.regex="^uid=([^,]+),ou=users,associatedDomain=$1,ou=domains,o=<basedn>$$" write
            by * none


    # Обратите внимание, что приведённое выше условие "by" должно быть в стиле "regex"
    # чтобы убедиться, что оно распространяется на DN, начинающиеся с шаблона "uid=<someuserid>" 
    # с заменой associatedDomain совпавшей подстрокой из условия "what".


    # А это правило позволяет пользователям "uid=<совпавший uid>" of "associatedDomain=<совпавший домен>"
    # вносить изменения (то есть добавлять, модифицировать, удалять) запись с DN, точно соответствующим
    # "uid=<совпавший uid>,ou=addressbook,associatedDomain=<совпавший домен>,ou=domains,o=<basedn>"
    # и ЛЮБУЮ запись-потомок этой записи


    access to dn.regex="^(.+,)?uid=([^,]+),ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$"
            by dn.exact,expand="uid=$2,ou=users,associatedDomain=$3,ou=domains,o=<basedn>" write
            by * none


    # Обратите внимание, что приведённое выше условие "by" использует стиль "exact"
    # с модификатором "expand", поскольку теперь всё условие может быть построено с помощью
    # совпавших подстрок из условия "what", следовательно компиляция и сопоставление "regex"
    # больше не требуется.

8.4.8. Советы по использованию регулярных выражений при контроле доступа

Всегда указывайте dn.regex=<шаблон>, когда Вы собираетесь использовать регулярные выражения. Если указать только dn=<шаблон>, то по умолчанию он будет соответствовать dn.exact<шаблон>.

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

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

    dn.regex=".*dc=example,dc=com"

небезопасно и затратно:

    dn.subtree="dc=example,dc=com"

чтобы включить dc=example,dc=com в шаблон для поиска совпадения,

    dn.children="dc=example,dc=com"

чтобы исключить dc=example,dc=com из шаблона для поиска совпадения, или

    dn.onelevel="dc=example,dc=com"

для соответствия только одному подуровню.

Всегда, когда это уместно, используйте ^ и $ в регулярных выражениях, поскольку без них ou=(.+),ou=(.+),ou=addressbooks,o=basedn будет совпадать с something=bla,ou=xxx,ou=yyy,ou=addressbooks,o=basedn,ou=addressbooks,o=basedn,dc=some,dc=org

Всегда используйте ([^,]+) чтобы указать соответствие точно одному RDN, поскольку (.+) может включать любое количество RDN; например, ou=(.+),dc=example,dc=com будет совпадать с ou=My,o=Org,dc=example,dc=com, а это, возможно, не то, что Вы ожидали.

Никогда не добавляйте rootdn в условия <by>. Во время операций, выполняемых от имени идентификационной сущности rootdn, ACL даже не обрабатываются (в противном случае, не было бы никакого смысла вообще определять rootdn).

Используйте термины. Термин users совпадает с пользователями, прошедшими аутентификацию, а термин anonymous совпадает с анонимными пользователями.

Не используйте форму dn.regex в условиях <by>, если Вам нужно только найти совпадение с диапазоном и/или заменить подстроку; используйте соответствия в стиле диапазона (например exact, onelevel, children или subtree) и модификатор стиля expand, чтобы указать расширение подстроки.

Например,

    access to dn.regex=".+,dc=([^,]+),dc=([^,]+)$"
      by dn.regex="^[^,],ou=Admin,dc=$1,dc=$2$$" write

хотя и верно, но может быть заменено на более безопасный и эффективный вариант

    access to dn.regex=".+,(dc=[^,]+,dc=[^,]+)$"
      by dn.onelevel,expand="ou=Admin,$1" write

где регулярное выражение в условии <what> является более компактным, а регулярное выражение в условии <by> заменено на гораздо более эффективное соответствие в стиле диапазона onelevel с расширением подстроки.

8.4.9. Предоставление и запрет доступа на основе факторов силы безопасности (ssf)

Вы можете ограничивать доступ, основываясь на факторах силы безопасности (SSF)

    access to dn="cn=example,cn=edu"
          by * ssf=256 read

0 (ноль) подразумевает, что защиты не требуется, 1 подразумевает только защиту целостности данных, 56 - требование использовать DES или другие слабые шифры, 112 - требование использовать triple DES или другие сильные шифры, 128 - требование использовать RC4, Blowfish или другие современные сильные шифры.

Другие возможности:

    transport_ssf=<n>
    tls_ssf=<n>
    sasl_ssf=<n>

Рекомендуется требования уровня безопасности 256.

Информацию по ssf можно найти в slapd.conf(5).

8.4.10. Если что-то работает не так, как ожидалось

Рассмотрим следующий пример:

    access to *
      by anonymous auth

    access to *
      by self write

    access to *
      by users read

Вам может показаться, что данные правила позволят любому пользователю пройти аутентификацию, прочитать любые записи из каталога и изменить свои данные, если аутентификация пройдена. Однако в этом примере будет работать только аутентификация, а ldapsearch никогда не вернёт данных. Проблема в том, что SLAPD применяет настройки доступа последовательно, строка за строкой, и останавливается на первой совпавшей части правил доступа (в данном случае: to *).

Чтобы получить то, что мы хотели, файл должен выглядеть следующим образом:

    access to *
      by anonymous auth
      by self write
      by users read

Основное правило: "сначала определяются более конкретные правила, а в конце - более общие".

Смотрите также slapd.access(5), а для поиска ошибок - loglevel 128 и slapacl(8).


8.5. Наборы - предоставление прав на основе взаимоотношений

Применение наборов лучше всего показать на примерах. В следующих подразделах представлено несколько примеров ACL для облегчения понимания их использования.

FAQ по применению наборов при контроле доступа: http://www.openldap.org/faq/data/cache/1133.html.


Примечание: Применение наборов пока считается экспериментальным.

8.5.1. Группы групп

Групповые ACL в OpenLDAP не имеют расширения для групп, содержащих группы, то есть групп, имеющих в качестве члена другую группу. Например:

 dn: cn=sudoadm,ou=group,dc=example,dc=com
 cn: sudoadm
 objectClass: groupOfNames
 member: uid=john,ou=people,dc=example,dc=com
 member: cn=accountadm,ou=group,dc=example,dc=com

 dn: cn=accountadm,ou=group,dc=example,dc=com
 cn: accountadm
 objectClass: groupOfNames
 member: uid=mary,ou=people,dc=example,dc=com

Если использовать стандартные групповые ACL с записями из примера выше и разрешить членам группы sudoadm писать куда-либо, запись mary не будет включена:

 access to dn.subtree="ou=sudoers,dc=example,dc=com"
         by group.exact="cn=sudoadm,ou=group,dc=example,dc=com" write
         by * read

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

 access to dn.subtree="ou=sudoers,dc=example,dc=com"
       by set="[cn=sudoadm,ou=group,dc=example,dc=com]/member* & user" write
       by * read

Данный ACL с набором (set) означает: взять DN cn=sudoadm, проверить его атрибут (атрибуты) member (где "*" означает рекурсивную проверку) и найти пересечение результата этой проверки с DN пользователя, прошедшего аутентификацию. Если итоговый результат не пуст, ACL считается совпавшим и предоставляется право на запись.

Следующий рисунок поясняет, как строится данный набор:

Рисунок 8.1: Заполнение набора рекурсивной группы

Сначала мы берём DN uid=john. Эта запись не имеет атрибута member, поэтому дальше мы не идём. Переходим к cn=accountadm. У этой записи есть атрибут member, содержащий uid=mary. Однако запись uid=mary, не имеет атрибута member, поэтому здесь мы снова останавливаемся. В конце операции получаем:

 {"uid=john,ou=people,dc=example,dc=com","uid=mary,ou=people,dc=example,dc=com"} & user

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

8.5.2. Групповые ACL, где членство указывается без применения DN-синтакса

Традиционные групповые ACL, и даже предыдущий пример с рекурсивными группами, требуют, чтобы члены группы указывались своими DN, а не просто именами пользователей.

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

Предположим, мы хотим позволить членам группы sudoadm вносить изменения в ветку ou=suders нашего дерева. Но, в данном случае, для определения членов группы мы используем атрибут memberUid:

 dn: cn=sudoadm,ou=group,dc=example,dc=com
 cn: sudoadm
 objectClass: posixGroup
 gidNumber: 1000
 memberUid: john

Мы не можем использовать групповые ACL с таким типом группы. Но желаемый контроль доступа можно установить с помощью наборов в ACL:

 access to dn.subtree="ou=sudoers,dc=example,dc=com"
       by set="[cn=sudoadm,ou=group,dc=example,dc=com]/memberUid & user/uid" write
       by * read

Мы используем простое пересечение, где сравниваем атрибут uid подключившегося (и прошедшего аутентификацию) пользователя с атрибутом memberUid группы. Если они совпадут, тогда пересечение окажется непустым и ACL предоставит доступ на изменение.

Данный рисунок иллюстрирует работу этого набора при подключении пользователя, прошедшего аутентификацию как uid=john,ou=people,dc=example,dc=com:

Рисунок 8.2: Применение набора с memberUid

В данном случае совпадение будет найдено. Однако, если же пользователь аутентифицировался бы как mary, доступ на изменение ou=sudoers был бы ей запрещён, поскольку её атрибут uid не перечислен в атрибуте memberUid группы.

8.5.3. Переход по ссылкам

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

Начнём с записи пользователя:

 dn: uid=john,ou=people,dc=example,dc=com
 uid: john
 objectClass: inetOrgPerson
 givenName: John
 sn: Smith
 cn: john
 manager: uid=mary,ou=people,dc=example,dc=com

С помощью наборов довольно просто составить ACL, позволяющий менеджеру (manager) обновлять некоторые атрибуты этой записи:

 access to dn.exact="uid=john,ou=people,dc=example,dc=com"
    attrs=carLicense,homePhone,mobile,pager,telephoneNumber
    by self write
    by set="this/manager & user" write
    by * read

В данном наборе this расширяется до записи, к которой нужно получить доступ, таким образом, при получении доступа к записи john, this/manager расширяется до uid=mary,ou=people,dc=example,dc=com. Если сама менеджер получает доступ к записи John, в ACL будет найдено совпадение и доступ на изменение указанных атрибутов будет предоставлен.

Подобное поведение можно получить и с помощью ключевого слова dnattr. Однако, применение наборов может способствовать дальнейшему усовершенствованию этого ACL. Допустим, мы хотим разрешить секретарю (secretary) менеджера также обновлять эти атрибуты. Вот как мы это сделаем:

 access to dn.exact="uid=john,ou=people,dc=example,dc=com"
    attrs=carLicense,homePhone,mobile,pager,telephoneNumber
    by self write
    by set="this/manager & user" write
    by set="this/manager/secretary & user" write
    by * read

Попробуем пояснить, что тут происходит, с помощью рисунка (записи сокращены для ясности):

Рисунок 8.3: Переход по записям с помощью набора

В этом примере, Jane - секретарь Mary, которая, в свою очередь, является менеджером John. Все эти взаимоотношения определяются атрибутами manager и secretary, оба из которых имеют синтаксис distinguishedName (то есть, полные DN). Таким образом, при получении доступа к записи uid=john, набор this/manager/secretary становится {"uid=jane,ou=people,dc=example,dc=com"} (следуя по ссылкам, как показано ниже):

 this = [uid=john,ou=people,dc=example,dc=com]
 this/manager = \
   [uid=john,ou=people,dc=example,dc=com]/manager = uid=mary,ou=people,dc=example,dc=com
 this/manager/secretary = \
   [uid=mary,ou=people,dc=example,dc=com]/secretary = uid=jane,ou=people,dc=example,dc=com

В итоге, когда Jane захочет получить доступ к записи John, ей будет предоставлено право на изменение указанных атрибутов. Более того, это касается и других записей, для которых Mary назначена менеджером.

Это всё, конечно, здорово и красиво, но не слишком ли мы развязываем руки секретарям? Думается, нужно побольше их ограничить. К примеру, давайте дадим такие привилегии только исполнительным секретарям:

 access to dn.exact="uid=john,ou=people,dc=example,dc=com"
   attrs=carLicense,homePhone,mobile,pager,telephoneNumber
   by self write
   by set="this/manager & user" write
   by set="this/manager/secretary &
           [cn=executive,ou=group,dc=example,dc=com]/member* &
           user" write
   by * read

Это практически тот же самый ACL, что и прежде, но сейчас мы также требуем, чтобы подключившийся пользователь был членом группы (возможно, вложенной) cn=executive.