Настройки LDAP
From LimeSurvey Manual
Общие сведения
Чтобы использовать эту функцию, необходимо включить поддержку LDAP в файле config.php и настроить параметры LDAP в файле config/ldap.php.
Включение LDAP в config.php
- $enableLdap: если вы хотите использовать функции LDAP в LimeSurvey, необходимо установить для этого параметра значение true (по умолчанию установлено значение false):
'config'=>array( 'debug'=>0, 'debugsql'=>0, 'enableLdap'=>true, )
Определение серверов LDAP
Сначала задайте параметры подключения к серверам LDAP в файле "application/config/ldap.php". Для каждого сервера доступны следующие параметры:
- $serverId: целое число, идентифицирующее этот сервер LDAP. Используется в определениях запросов для привязки сервера к конкретному запросу;
- $ldap_server[$serverId]['server']: IP-адрес или DNS-имя сервера LDAP. Если вы используете защищённые SSL-соединения (LDAPs или LDAP+Start-TLS), это имя должно совпадать с CN сертификата сервера (или с SubjectAlternativeName);
- $ldap_server[$serverId]['port']: TCP-порт, который прослушивает сервер LDAP. Обычно это 389 для LDAP и LDAP+Start-TLS и 636 для LDAPs;
- $ldap_server[$serverId]['protoversion']: может принимать значения 'ldapv2' или 'ldapv3' в зависимости от протокола, поддерживаемого вашим сервером. Предпочтительным является протокол 'ldapv3'. Однако если вы хотите использовать шифрованные соединения, учтите, что LDAPs поддерживается в режиме 'ldapv2', тогда как Start-TLS является методом шифрования для 'ldapv3';
- $ldap_server[$serverId]['encrypt']: определяет используемый метод шифрования. 'ldaps' поддерживается для серверов 'ldapv2', 'start-tls' — для серверов 'ldapv3'. Ключевое слово 'none' используется для обмена данными по LDAP без шифрования;
- Не забывайте, что при шифровании 'ldaps' или 'start-tls' веб-сервер должен иметь возможность проверить сертификат сервера LDAP. Поэтому необходимо указать ваш центр сертификации в библиотеке openldap (в Linux это обычно делается в файле /etc/openldap/ldap.conf).
- $ldap_server[$serverId]['referrals']: логический параметр, определяющий, нужно ли следовать по ссылкам (referrals) (для ActiveDirectory используйте false);
- $ldap_server[$serverId]['encoding']: необязательный параметр, задающий кодировку, которую каталог LDAP использует для хранения строк. Обычно этот параметр настраивать не нужно, так как кодировка по умолчанию, 'utf-8', является стандартной для каталогов LDAP. Однако если вы используете Active Directory и у вас возникают проблемы при импорте строк с диакритическими знаками, попробуйте задать кодировку, используемую в вашем регионе (например, 'cp850' для Западной Европы). Полный список поддерживаемых кодировок можно найти в раскрывающемся списке "Кодировка файла" в интерфейсе импорта токенов из CSV файла.
Далее необходимо определить, какая аутентификация требуется для доступа к каталогу. Если разрешён 'анонимный' доступ, НЕ задавайте два следующих параметра, в противном случае задайте их соответствующим образом:
- $ldap_server[$serverId]['binddn']: DN пользователя 'LDAP', которому разрешено читать каталог;
- $ldap_server[$serverId]['bindpw']: пароль указанного выше пользователя LDAP.
Если необходимо определить другие серверы LDAP, добавьте следующую строку, чтобы увеличить serverID, и задайте новые параметры:
- $serverId++.
Определение запросов в config/ldap.php
Внимание: если в одном из этих параметров требуется имя атрибута ldap, используйте только имена в нижнем регистре: например, displayname, а НЕ displayName.
Примеры конфигурации см. в файле config/ldap.php.
Простые запросы
Начнём с простых запросов. Эти запросы фильтруют записи LDAP только на основе их собственных атрибутов и расположения. Обычно их достаточно для запросов к ActiveDirectory.
- $query_id: идентификатор запроса LDAP;
- $ldap_queries[$query_id]['ldapServerId']: привязывает запрос к конкретному серверу;
- $ldap_queries[$query_id]['name']: строка, описывающая запрос. Она будет отображаться в интерфейсе;
- $ldap_queries[$query_id]['userbase']: корневой DN, используемый для поиска пользователей;
- $ldap_queries[$query_id]['userfilter']: фильтр, используемый для выбора потенциальных записей пользователей. Он должен быть заключён в круглые скобки;
- $ldap_queries[$query_id]['userscope']: область поиска пользователей в LDAP ('base', 'one' или 'sub');
- $ldap_queries[$query_id]['firstname_attr']: атрибут Ldap, который будет сопоставлен полю имени в записи токена;
- $ldap_queries[$query_id]['lastname_attr']: атрибут Ldap, который будет сопоставлен полю фамилии в записи токена;
- $ldap_queries[$query_id]['email_attr']: атрибут Ldap, который будет сопоставлен полю адреса E-mail в записи токена.
При желании можно получить из каталога дополнительную информацию:
- $ldap_queries[$query_id]['token_attr']: атрибут Ldap, который будет сопоставлен коду токена;
- $ldap_queries[$query_id]['language']: атрибут Ldap, который будет сопоставлен коду языка пользователя;
- $ldap_queries[$query_id]['attr1']: атрибут Ldap, который будет сопоставлен полю attribute_1;
- $ldap_queries[$query_id]['attr2']: атрибут Ldap, который будет сопоставлен полю attribute_2.
Комбинированные запросы групп с членами в виде DN
Теперь рассмотрим, как определить более сложный запрос.
Следующие запросы используют первый поиск LDAP, который просматривает группы LDAP. Группа LDAP — это запись LDAP, содержащая ссылки на записи пользователей в виде:
- идентификаторов пользователей (например, так устроены posixGroups) ==> см. следующий раздел
- или DN пользователей (например, так устроены groupofnames и groupofuniquenames) ==> см. ниже
Здесь мы рассматриваем группы, содержащие DN пользователей:
- определите $query_id, $ldap_queries[$query_id]['ldapServerId'], $ldap_queries[$query_id]['name'], как описано выше.
Затем определите параметры фильтра групп:
- $ldap_queries[$query_id]['groupbase']: корневой DN, с которого нужно начинать поиск записей групп;
- $ldap_queries[$query_id]['groupfilter']: фильтр LDAP, который будет выбирать потенциальные записи групп;
- $ldap_queries[$query_id]['groupscope']: область поиска групп в LDAP ('one', 'base' или 'sub');
- $ldap_queries[$query_id]['groupmemberattr']: имя атрибута LDAP в записи группы, который будет содержать ссылки на записи пользователей;
- $ldap_queries[$query_id]['groupmemberisdn']: TRUE.
На этом этапе всё настроено так, чтобы первый поиск LDAP находил пользователей, соответствующих выбранным группам. Однако можно ограничить круг выбираемых "кандидатов в пользователи", применив к ним ещё один фильтр. Это, разумеется, необязательно:
- $ldap_queries[$query_id]['userbase']: базовый DN для поиска пользователей в LDAP (будут выбраны только кандидаты, соответствующие этой базе);
- $ldap_queries[$query_id]['userscope']: область поиска пользователей в LDAP (будут выбраны только кандидаты, соответствующие userbase+scope);
- $ldap_queries[$query_id]['userfilter']: фильтр, который применяется к каждой записи кандидата в пользователи (по её атрибутам) для дополнительного отбора.
Комбинированные запросы групп с членами в виде UID
Теперь рассмотрим, как определить комбинированный запрос группы, когда членами группы являются UID пользователей, а не их DN.
Как и запросы групп с членами в виде DN, эти запросы используют первый поиск LDAP, который ищет записи групп LDAP и получает их членов. Затем значения этих членов используются в фильтре поиска пользователей для поиска соответствующих записей. Поэтому необходимо настроить ещё один параметр, определяющий атрибут в записи пользователя, который должен совпадать с UID члена, найденным в группах.
Рассмотрим необходимые параметры:
- определите $query_id, $ldap_queries[$query_id]['ldapServerId'], $ldap_queries[$query_id]['name'], как описано выше
Затем определите параметры фильтра групп:
- $ldap_queries[$query_id]['groupbase']: корневой DN, с которого нужно начинать поиск записей групп;
- $ldap_queries[$query_id]['groupfilter']: фильтр LDAP, который будет выбирать потенциальные записи групп;
- $ldap_queries[$query_id]['groupscope']: область поиска групп в LDAP ('one', 'base' или 'sub');
- $ldap_queries[$query_id]['groupmemberattr']: имя атрибута LDAP в записи группы, который будет содержать ссылки на записи пользователей;
- $ldap_queries[$query_id]['groupmemberisdn']: FALSE;
- $ldap_queries[$query_id]['useridattr']: имя атрибута пользователя, который должен совпадать с UID, найденным среди членов группы.
На этом этапе всё настроено так, чтобы первый поиск LDAP находил UID пользователей, соответствующие выбранным группам, а фильтр поиска пользователей будет заполнен автоматически.
Однако вы можете ограничить выбор «кандидатов в пользователи», дополнив автоматический фильтр пользователей, сформированный из UID членов группы. Это, разумеется, необязательно:
- $ldap_queries[$query_id]['userbase']: базовый DN для поиска пользователей в LDAP (будут выбраны только кандидаты, соответствующие этой базе);
- $ldap_queries[$query_id]['userscope']: область поиска пользователей в LDAP (будут выбраны только кандидаты, соответствующие userbase и области поиска);
- $ldap_queries[$query_id]['userfilter']: фильтр, который применяется к записи каждого кандидата (по её атрибутам) для дополнительного отбора.
Что насчёт Active Directory?
Active Directory (AD) — это реестр Microsoft, к которому можно обращаться с запросами по протоколу LDAP.
Поэтому его содержимое можно использовать для запросов токенов в LimeSurvey, но для этого нужно знать, как организован AD.
- Корневая база LDAP: dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1
==> Например, если вашей компании принадлежит DNS-домен 'my-company.com', а ваш домен Windows называется 'employees', то корневая база — dc=employees,dc=my-company,dc=com
- Пользователи и группы пользователей хранятся в cn=Users,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1 (обратите внимание: это не ou=users);
- Группы Active Directory:
- Объекты групп содержат DN членов группы в атрибуте 'member';
- Членство в группах также хранится в атрибуте memberOf каждой записи пользователя. Этот атрибут содержит DN групп, к которым принадлежит пользователь;
- некоторые группы находятся в CN=Builtin,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1:
- Например: cn=Administrator,CN=Builtin,dc=my_windows_domain_name,dc=dns_suffix2,dc=dns_suffix1;
В некоторых случаях запросить данные из Active Directory не так просто, поэтому ниже приведён пример конфигурации для получения некоторой информации из Active Directory:
//Connection to the active directory Server:
$serverId=0;
$ldap_server[$serverId]['server'] = "10.10.10.10";
$ldap_server[$serverId]['port'] = "389";
$ldap_server[$serverId]['protoversion'] = "ldapv2";
$ldap_server[$serverId]['encrypt'] = "none"; // Most AD LDAP servers will not have encryption set by default
$ldap_server[$serverId]['referrals'] = false;
$ldap_server[$serverId]['binddn'] = "domain\\user";
$ldap_server[$serverId]['bindpw'] = "userpassword";
//$ldap_server[$serverId]['binddn'] = "CN=user,OU=user_group,DC=xxx,DC=yyy"; this one will not work with active directory, that´s why you need to use "domain\\user"
//Here is a sample query for getting all active users of an active directory:
$query_id=0;
$ldap_queries[$query_id]['ldapServerId'] = 0;
$ldap_queries[$query_id]['name'] = 'Staff with an enabled account';
$ldap_queries[$query_id]['userbase'] = 'OU=USER_GROUP,DC=xxx,DC=yyy';
$ldap_queries[$query_id]['userfilter'] = '(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
//AD has no "account is enabled" attribute, so the filter excludes accounts that have the "disabled" flag (bit 2) set in userAccountControl
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = ''; // Leave empty for Auto Token generation by phpsv
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
//You can also filter on group membership through the memberOf attribute of the user. Here is a sample query for getting all active users that are member of the group "samplegroup" in active directory:
$query_id++;
$ldap_queries[$query_id]['ldapServerId'] = 0;
$ldap_queries[$query_id]['name'] = 'All members of samplegroup';
$ldap_queries[$query_id]['userbase'] = 'OU=USER_GROUP,DC=xxx,DC=yyy';
$ldap_queries[$query_id]['userfilter'] = '(&(objectClass=user)(memberOf=CN=samplegroup,OU=Group Global,OU=USER_GROUP,DC=xxx,DC=yyy)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = ''; // Leave empty for Auto Token generation by phpsv
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
Другой пример запроса пользователей:
$ldap_queries[$query_id]['userfilter'] = '(&(objectCategory=Person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'; // AD doesn't recognise enabled accounts in the normal way, so instead, we check users are not disabled
- Рекомендуем добавить (mail=*) в фильтры пользователей, чтобы пропускать пользователей без адреса e-mail.
Пример запроса групп:
$ldap_queries[$query_id]['groupfilter'] = '(&(objectClass=group)(cn=Domain Admins))'; // AD doesn't use the standard attribute name for groups, so use this example instead.
Автоматическое создание одного запроса для каждой группы Active Directory
Если группы участников ваших опросов хранятся в одном подразделении (OU), не нужно писать отдельный запрос для каждой группы. Следующий код выводит список всех групп в этом подразделении и создаёт по одному запросу для каждой группы. Запрос включает всех активных членов группы, в том числе членов вложенных групп.
Добавьте код в config/ldap.php после определений серверов и запросов и перед строкой //DO NOT CHANGE BELOW HERE. Затем задайте четыре переменные в начале кода:
- $groupServerId: значение $serverId сервера, который нужно использовать;
- $groupContainer: DN подразделения (OU), в котором находятся группы. В список попадают только группы, расположенные непосредственно в этом OU, но не в его дочерних OU;
- $groupUserBase: корневой DN, в котором выполняется поиск членов групп;
- $groupNameAttr: атрибут группы (в нижнем регистре), который используется как название запроса в интерфейсе, например 'info' (поле «Заметки» в «Active Directory — пользователи и компьютеры») или 'description'. Если у группы нет значения в этом атрибуте, используется её cn.
$groupServerId = 0;
$groupContainer = 'OU=Survey groups,DC=xxx,DC=yyy';
$groupUserBase = 'DC=xxx,DC=yyy';
$groupNameAttr = 'info';
$srv = $ldap_server[$groupServerId];
$ds = ldap_connect(($srv['encrypt'] == 'ldaps' ? 'ldaps://' : 'ldap://') . $srv['server'] . ':' . $srv['port']);
if ($ds) {
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $srv['protoversion'] == 'ldapv3' ? 3 : 2);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
// Never send the bind password if Start-TLS was requested but failed
$secure = $srv['encrypt'] != 'start-tls' || @ldap_start_tls($ds);
$bound = $secure && (empty($srv['binddn']) ? @ldap_bind($ds) : @ldap_bind($ds, $srv['binddn'], $srv['bindpw']));
$result = $bound ? @ldap_list($ds, $groupContainer, '(objectCategory=group)', array('cn', $groupNameAttr)) : false;
$groups = $result ? ldap_get_entries($ds, $result) : array('count' => 0);
for ($i = 0; $i < $groups['count']; $i++) {
$query_id++;
$ldap_queries[$query_id]['ldapServerId'] = $groupServerId;
$ldap_queries[$query_id]['name'] = isset($groups[$i][$groupNameAttr][0]) ? $groups[$i][$groupNameAttr][0] : $groups[$i]['cn'][0];
$ldap_queries[$query_id]['userbase'] = $groupUserBase;
// Enabled users with an email address that are members of the group, directly or through nested groups
$ldap_queries[$query_id]['userfilter'] = '(&(objectCategory=person)(objectClass=user)(mail=*)'
. '(!(userAccountControl:1.2.840.113556.1.4.803:=2))'
. '(memberOf:1.2.840.113556.1.4.1941:=' . ldap_escape($groups[$i]['dn'], '', LDAP_ESCAPE_FILTER) . '))';
$ldap_queries[$query_id]['userscope'] = 'sub';
$ldap_queries[$query_id]['firstname_attr'] = 'givenname';
$ldap_queries[$query_id]['lastname_attr'] = 'sn';
$ldap_queries[$query_id]['email_attr'] = 'mail';
$ldap_queries[$query_id]['token_attr'] = '';
$ldap_queries[$query_id]['language'] = '';
$ldap_queries[$query_id]['attr1'] = '';
$ldap_queries[$query_id]['attr2'] = '';
}
ldap_unbind($ds);
}
Что необходимо учитывать:
- Код подключается к каталогу при каждом открытии или отправке страницы «Импортировать участников из LDAP», поэтому новые группы появляются без изменения конфигурации. Если подключение или привязка (bind) не удалась, на странице отображаются только запросы, которые вы определили вручную.
- Правило сопоставления 1.2.840.113556.1.4.1941 (LDAP_MATCHING_RULE_IN_CHAIN) разрешает вложенные группы. Оно существует только в Active Directory, поэтому этот пример не работает с другими серверами LDAP. Для таких серверов вместо него используйте комбинированный запрос групп для каждой группы.
- Чтобы в список попадали и группы из дочерних OU в $groupContainer, замените ldap_list на ldap_search.
- Чтобы получить группы из нескольких OU, повторите код с другими значениями четырёх переменных.
Дополнительную информацию о структуре LDAP в Active Directory см. в разделах Active Directory Architecture и Active Directory Technical Specification.