Налаштування 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 (зазвичай це робиться у файлі /etc/openldap/ldap.conf під Linux).
- $ldap_server[$serverId]['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, який буде зіставлено з полем електронної адреси в записі токена.
За бажанням можна отримати з каталогу додаткову інформацію:
- $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+scope);
- $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=*) до фільтрів користувачів, щоб пропускати користувачів без електронної адреси.
Приклад запиту груп:
$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), вам не потрібно писати запит для кожної групи. Наведений нижче код виводить список усіх груп у цій OU та створює один запит для кожної групи. Запит охоплює всіх активних членів групи, зокрема членів вкладених груп.
Додайте код до config/ldap.php після визначень сервера та запитів і перед рядком //DO NOT CHANGE BELOW HERE. Потім задайте чотири змінні на початку:
- $groupServerId: $serverId сервера, який слід використовувати;
- $groupContainer: DN організаційної одиниці (OU), що містить групи. У списку з'являються лише групи, розташовані безпосередньо в цій OU, а не групи у вкладених OU;
- $groupUserBase: кореневий DN, у межах якого здійснюється пошук членів груп;
- $groupNameAttr: атрибут групи (малими літерами), який використовується як назва запиту в GUI, наприклад 'info' (поле «Notes» в «Active Directory Users and Computers») або '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» відкривають або надсилають, тому нові групи з'являються без зміни конфігурації. Якщо підключення або прив'язка не вдаються, на сторінці відображаються лише запити, які ви визначили вручну.
- Правило відповідності 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.