Никогда не думал, что столкнусь с такой задачей, тем не в один прекрасный день возникла необходимость создать корпоративный портал для внутреннего использования. В организации есть домен и куча пользователей в этом домене. Было бы безусловно круто не плодить сущности (не создавать заново учетки для пользователей), а как-то привязаться к домену.

Сначала был долгий мозговой штурм и решали на чем делать этот портал - то ли вручную на PHP или ASP, то ли использовать какой-то готовый узкоспециализированный продукт и использовать его. При том, что этот продукт должен быть бесплатным. Естественно, никакого вменяемого бесплатного решения именно для корпоративного портала не нашлось, а писать все это вручную не было особого желания, возникла мысль - "а может Joomla?"

Сначала эту идею пытались закопать, ибо "не круто" но чем дальше обсуждали, тем больше чаша весов склонялась в пользу Joomla. Проверенного, распространенного и абсолютно бесплатного. Осталось решить только одно - как подружить Joomla с Active Directory. Оказалось, это возможно, хотя пришлось немного повозиться.

Я не буду расписывать такие простые истины, например, как установить Joomla на локальный хост и прочее, сразу к делу.

1 этап - правка файла PHP.ini

Нужно раскомментировать строчку:

extension=php_ldap.dll

Это библиотека, благодаря которой возможно взаимодействие Joomla с доменом Active Directory по протоколу LDAP.

2 этап - настройка плагина "Авторизация LDAP"

Идем в менеджер плагинов и ищем среди них "Авторизация LDAP", активируем его.

Предположим, что домен у нас называется domain.local, адрес контроллера домена - 192.168.0.1.

В настройках плагина нужно сделать следующие настройки (по крайней мере, у меня так заработало:)

Параметр Значение Комментарий
Хост 192.168.0.1 IP-адрес контроллера домена
Порт 389 не трогаем
Выполнять LDAP3 ДА
Выполнять TLS НЕТ
Следовать перенаправлениям НЕТ
Метод авторизации Привязать и найти При успешной авторизации Joomla создаст свою внутреннюю учетку, которую можно будет потом поместить в ту или иную группу доступа. К сожалению, группы пользователей из Active Directory Joomla подтягивать не умеет. Если нашли способ сделать это, поделитесь в комментариях к статье!
Базовый DN dc=domain,dc=local Разделяем домен на уровни и перед каждым уровнем пишем dc=..., между ними никаких пробелов, только запятые
Строка поиска sAMAccountName= Регистр имеет значение!
Пользовательский DN оставляем пустым
Имя пользователя подлючения Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра. Имя доменного администратора
Пароль подключения qwerty123 Соответственно, пароль доменного администратора
Map: Full Name displayName То, как на сайте будет высвечиваться имя пользователя
Map: Email mail то же самое - емэйл
Map: User ID uid идентификатор пользователя

Сохраняем настройки, открываем сайт и пытаемся залогиниться. При этом вводить имя доменного пользователя указываем без @domain.local - все должно заработать!

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

  • Веб-сервер - Xampp 5.6.15 (PHP 5.6.15)
  • Joomla 3.6.3
  • Контроллер домена Windows Server 2008 R2

добавлено 26.10.2016

Замечена странность - Joomla не хочет авторизовать пользователей, чей логин в домене не совпадает с адресом электронной почты в Exchange. При попытке авторизации выдается сообщение об ошибке:

Предупреждение

У вас нет права доступа к закрытой части сайта.

Решение данной проблемы пока не найдено. Если вы знаете, как это преодолеть, пожалуйста, поделитесь в комментариях.

Работаем с AD на PHP

Чтение данных. Часть 1. Подключение к AD, запрос и обработка данных

Серия контента:

Для выполнения основных операций по работе с AD, таких как добавление или удаление пользователя, изменение данных или членства в группах, и в особенности для массовых операций (например, сформировать список всех пользователей по отделам) вовсе не обязательно изучать Visual Basic или PowerShell - для этого достаточно знания PHP (а также наличия пользователя с необходимыми правами).

Часто используемые сокращения:

  • AD - Active Directory (служба каталогов);
  • LDAP - облегченный протокол доступа к каталогу (lightweight directory access protocol);
  • DN - отличительное имя (distinguished name).

В первых частях цикла, опубликованных в июне месяце (, , ) рассказывалось о том, как прочитать данные сервера AD, обращаясь к нему как к обычному LDAP-серверу с помощью стандартной программы ldapsearch и скрипта, написанного на языке Bourne Shell. Надо сказать, Bourne Shell не слишком приспособлен для такой работы: даже для достаточно простой операции формирования текстового файла из двух колонок приходится идти на весьма нетривиальные ходы. Поэтому совершенно естественно попробовать переписать его на языке высокого уровня, например, на PHP.

Конфигурационный файл

Для работы скрипта используется практически тот же самый конфигурационный файл. Его содержание приведено в Листинге 1.

Листинг 1. Конфигурационный файл скрипта phpldapread.php
#LDAP сервер для подключения ldap_server=10.54.200.1 #Base DN для подключения ldap_basedn="dc=shelton,dc=int" #Bind DN для подключения [email protected] #Пароль для пользователя, от имени которого будет выполняться подключение ldap_password="cXdlcnR5YXNkZgo 1" #Фильтр отбора записей. Он означает: отобрать объекты типа Пользователь, # у которых не установлено свойство "Заблокировать учетную запись" ldap_common_filter="(&(!(userAccountControl:1.2.840.113556.1.4.803:=2)) (sAMAccountType=805306368))" #Игнорировать перечисленных пользователей - это системные объекты ignore_list="SQLAgentCmdExec,SMSService,SMSServer_001, wsus" #Каталог, куда будет сохранен файл etcdir=/tmp #Имя файла со списком sarglist=sargusers

Зависимости, вспомогательные функции

Для работы скрипта потребуются дополнительные компоненты pear-Config и pear-Console_Getopt, а также расширение языка php-ldap. Pear-Config потребуется для чтения конфигурационного файла, pear-Console_Getopt - для разбора параметров командной строки. Надо сказать, рассматривается не весь скрипт: такие вопросы, как чтение конфигурационного файла, отображение справки или разбор командной строки являются вопросами уже достаточно хорошо описанными, поэтому соответствующие функции будут опущены, полную версию скрипта можно скачать с . Рассмотрено будет только то, что непосредственно относится к чтению данных из AD, как сервера LDAP, и некоторые нестандартные вспомогательные функции.

Функция обратного преобразования пароля приведена в Листинге 2. Вся роль так называемой "защиты" - предотвратить случайную утечку (ту, что называется eyedropper) и не более того.

Листинг 2. Функция обратного преобразования пароля.
/* * Обратное преобразование пароля * @param string $converted преобразованный пароль * @return string $passwd пароль в текстовой форме */ function demux_passwd($converted) { $_conved = explode(" ", $converted); $_passwd = ""; if ($_conved != 0) for (;$_conved != 0; $_conved--) { $_conved = $_conved . "="; } $_passwd = base64_decode($_conved); return rtrim($_passwd); }

Ничего особо интересного, конечно же, здесь нет: как уже было сказано в предыдущих частях, в конфигурационном файле хранится пароль, преобразованный в base64, заполнители отброшены и заменены числом. Эта функция выполняет обратное преобразование.

Функция перекодировки из UTF-8 в KOI8-R приведена в Листинге 3. Необходимость в этой функции возникает вследствие того, что консоль во FreeBSD не использует UTF-8.

Листинг 3. Функция перекодировки строки из UTF-8 в KOI8-R
/* * Перекодировать строку из UTF-8 в KOI8-R * @param string $source строка в кодировке UTF-8 * @return string $dest строка в кодировке KOI8-R */ function _from_utf8($source) { $converted = iconv("UTF-8", "KOI8-R", $source); return($converted); }

Кроме того, используется совершенно неинтересная функция safe_logger, в задачу которой входит вывод сообщений в лог или на консоль с завершением или без завершения работы скрипта. Все эти функции сохранены в файле utils.php.

Подключение к AD

Для подключения к AD используется функция ldap_server_connect, приведенная в Листинге 4. Функция выполняет все операции по подключению и возвращает идентификатор соединения для работы с сервером. Функция сохранена в отдельный файл ldapquery.php

Листинг 4. Функция подключения к серверу AD
require_once $PATH_LIB."/utils.php"; /* * Подключение к серверу LDAP * @param array $_config массив параметров конфигурации * @return resource $ldapconn идентификатор соединения с LDAP-сервером */ function ldap_server_connect($_config) { // Получить пароль в текстовом виде $_ldap_pwd = demux_passwd($_config["root"]["ldap_password"]); // Установить соединение с сервером if (!$ldapconn = ldap_connect($_config["root"]["ldap_server"])) safe_logger(sprintf("Невозможно подключиться к LDAP-серверу %s", $_config["root"]["ldap_server"]), "DIE"); // Для подключения к AD Windows 2003 и выше нужно установить эти опции ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); // Авторизоваться на сервере ldap_bind($ldapconn, $_config["root"]["ldap_binddn"], $_ldap_pwd); return $ldapconn; }

На что хотелось бы обратить внимание здесь.

Во-первых, опции LDAP_OPT_PROTOCOL_VERSION ("версия протокола") и LDAP_OPT_REFERRALS ("отключить реферальные ссылки") должны быть обязательно установлены соответственно в 3 и 0 - без них можно увидеть странное: авторизация на сервере пройдет, но любой поиск будет возвращать ровно нуль записей.

Во-вторых, Bind DN должен быть задан в точности как в конфигурационном файле и никаким другим образом. Неверным будет в том числе и указание полного DN.

Запрос данных из AD

Для запроса данных из AD разработана отдельная функция ldap_data_query. Сделано это в основном потому, что данные, содержащие не-ASCII символы (а таких в нормальном AD большинство), хранятся в кодировке UTF-8. Поскольку консоль FreeBSD ограниченно поддерживает UTF-8, пришлось пойти на некоторые преобразования.

Отбор данных из AD проводится функцией ldap_search, которая принимает в числе других параметров одномерный массив с атрибутами, которые необходимо получить. Но для того, чтобы указать, следует ли перекодировать значение этого атрибута, функция получает двумерный массив, в котором каждый элемент сам есть массив, состоящий из элементов с индексами name и recode.

Вид массива атрибутов, который получает на входе функция, показан в Листинге 5 (частично).

Листинг 5. Массив параметров, передаваемый функции запроса данных.
array(2) { => array(2) { ["name"]=> string(2) "cn" ["recode"]=> string(4) "true" } ... }

Собственно функция запроса данных приведена в Листинге 6.

Листинг 6. Функция запроса данных из AD.
require_once $PATH_LIB."/utils.php"; require_once $PATH_LIB."/ldapconnect.php"; /* * Запросить данные с сервера LDAP * @param array $_config Массив с конфигурационными данными * @param resource $ldapconn Идентификатор подключения к серверу LDAP * @param array $attribute Массив атрибутов для запроса из LDAP * @return array $ldapdata Данные с сервера LDAP */ function ldap_data_query($_config, $ldapconn, $attribute) { $oneadd = array(); $myrecode = array(); $myattrs = array(); // Для запроса данных мы создаем одномерный массив foreach ($attribute as $oneattr) $myattrs = $oneattr["name"]; // Запросим данные, используя общий фильтр отбора из конфигурационного файла $result = ldap_search($ldapconn, $_config["root"]["ldap_basedn"], $_config["root"]["ldap_common_filter"], $myattrs); // Прочитаем все отобранные записи $info = ldap_get_entries($ldapconn, $result); // Выведем их количество в лог safe_logger(sprintf("Read %d records from server %s", $info["count"], $_config["root"]["ldap_server"]), ""); // Создадим двумерный массив с выходными данными // Каждый элемент массива есть массив, в элементах которого ключ - имя атрибута, // а данные - значение атрибута; перекодированные, если надо for ($i = 0; $i < $info["count"]; $i++) { for ($j = 0, $k = count($attribute); $j < $k; $j++) { $myattr = $attribute[$j]["name"]; if (isset($info[$i][$myattr])) { if ($attribute[$j]["recode"] == "true") $myrecode[$myattr] = _from_utf8($info[$i][$myattr]); else $myrecode[$myattr] = $info[$i][$myattr]; } else $myrecode[$myattr] = ""; $oneadd[$i] = $myrecode; } } return $oneadd; }

Из двумерного массива параметров формируется одномерный для функции ldap_search потом запрашиваются данные. Данные возвращаются в виде массива, каждый элемент которого имеет вид, приведенный в Листинге 7.

Листинг 7. Один элемент массива данных, возвращаемого функцией ldap_get_entries.
=> array(6) { ["cn"]=> array(2) { ["count"]=> int(1) => string(13) "Administrator" } => string(2) "cn" ["samaccountname"]=> array(2) { ["count"]=> int(1) => string(13) "Administrator" } => string(14) "samaccountname" ["count"]=> int(2) ["dn"]=> string(43) "CN=Administrator,CN=Users,DC=shelton,DC=net" }

Как видно, это даже не двумерный, а трехмерный массив. На первом уровне - запрошенные данные, на втором - атрибуты одного объекта, на третьем - строки многострочного атрибута, которым на всякий случай считаются все строковые атрибуты. Также в каждом элементе первого уровня присутствует элемент второго уровня dn, который содержит полный DN данного объекта - это нам очень пригодится в дальнейшем. Выходной же массив куда проще, вид одного элемента приведен на Листинге 8. Здесь нарочно использован объект с не-ASCII данными, чтобы показать тот факт, что данные были перекодированы.

Листинг 8. Элемент выходного массива.
=> array(2) { ["cn"]=> string(11) "Просто Юзер" ["samaccountname"]=> string(10) "prostouser" }

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

Заключение

Как видно из данной статьи, PHP значительно упрощает работу с LDAP-сервером, позволяя отказаться от зубодробительных конструкций, связанных с хранением данных во временных файлах, заменяя их куда более удобным представлением массивов в памяти, позволяя "на ходу" перекодировать в другую кодовую страницу, и значительно облегчая отладку скрипта.

16 years ago

Try this script if you don"t know how to add an user in the AD Win2K.
To have more informations about the attributes, open the adsiedit console in the Support Tools for Win2K.

$adduserAD["cn"] =
$adduserAD["instancetype"] =
$adduserAD["objectclass"] = "top";
$adduserAD["objectclass"] = "person";
$adduserAD["objectclass"] = "organizationalPerson";
$adduserAD["objectclass"] = "user";
$adduserAD["displayname"] =
$adduserAD["name"] =
$adduserAD["givenname"] =
$adduserAD["sn"] =
$adduserAD["company"] =
$adduserAD["department"] =
$adduserAD["title"] =
$adduserAD["description"] =
$adduserAD["mail"] =
$adduserAD["initials"] =
$adduserAD["samaccountname"] =
$adduserAD["userprincipalname"] =
$adduserAD["profilepath"] =
$adduserAD["manager"] = ***Use DistinguishedName***

if (!($ldap = ldap_connect("localhost"))) {
die ("Could not connect to LDAP server");
}
if (!($res = @ldap_bind($ldap, "[email protected]", $password))) {
die ("Could not bind to the LDAP account");
}
if (!(ldap_add($ldap, "CN=New User,OU=OU Users,DC=pc,DC=com", $adduserAD))){
echo "There is a problem to create the account
echo "Please contact your administrator !";
exit;
}
ldap_unbind($ldap);

12 years ago

Here is how to add a user with a hashed MD5 password to OpenLDAP. I used this technique to migrate Drupal accounts into OpenLDAP for a single-sign-on solution.

The trick to it is to tell OpenLDAP the hash type (e.g. {MD5}) before the password, and also to base64 encode the BINARY hashed result. You cannot just base64 encode what is returned by PHP"s md5() or sha() hash functions, because they return a hexadecimal text string. First you must use pack("H*", $hash_result) to make that a binary string, THEN you can base64 encode it.

Here is complete code for connecting and adding a user with a hashed password. You don"t have to use {MD5}, you could pick a different hash if that is what you have. The output from one of these hashed passwords will look like this: {md5}bdwD04RS9xMDGVi1n/H36Q==

Finally some caveats: This technique will not work if you hashed the password using a salt value (but Drupal does not). This technique will also certainly not work with active directory, where passwords can definitely only be set over SSL connections and hashing probably works differently.

$ds = ldap_connect($serverAddress);
if ($ds) {
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); // otherwise PHP defaults to ldap v2 and you will get a Syntax Error!
$r = ldap_bind($ds, $managerDN, $managerPassword);
$ldaprecord["cn"] = $newuser_username;
$ldaprecord["givenName"] = $newuser_firstname;
$ldaprecord["sn"] = $newuser_surname;
// put user in objectClass inetOrgPerson so we can set the mail and phone number attributes
$ldaprecord["objectclass"] = "person";
$ldaprecord["objectclass"] = "organizationalPerson";
$ldaprecord["objectclass"] = "inetOrgPerson";
$ldaprecord["mail"] = $newuser_email_address;
$ldaprecord["telephoneNumber"] = $newuser_phone_number;
// and now the tricky part, base64 encode the binary hash result:
$ldaprecord["userPassword"] = "{MD5}" . base64_encode(pack("H*",$newuser_md5hashed_password));
// If you have the plain text password instead, you could use:
// $ldaprecord["userPassword"] = "{MD5}" . base64_encode(pack("H*",md5($newuser_plaintext_password)));
$r = ldap_add($ds, $base_user_dn, $ldaprecord);
} else { die "cannot connect to LDAP server at $serverAddress."; }

11 years ago

I created a simple function that can be called to create global distribution groups in Active Directory:

function ldap_createGroup ($object_name , $dn , $members , $ldap_conn )
{
$addgroup_ad [ "cn" ]= " $object_name " ;
$addgroup_ad [ "objectClass" ][ 0 ] = "top" ;
$addgroup_ad [ "objectClass" ][ 1 ] = "group" ;
$addgroup_ad [ "groupType" ]= "2" ;
$addgroup_ad [ "member" ]= $members ;
$addgroup_ad [ "sAMAccountName" ] = $object_name ;

Ldap_add ($ldap_conn , $dn , $addgroup_ad );

If(ldap_error ($ldap_conn ) == "Success" )
return true ;
else
return false ;
}
?>

You can call this function using the follow code:

$ldap_conn = ldap_bind ();
$object_name = "Test Group" ;
$dn = "CN=" . $object_name . ",OU=PathToAddGroupTo,OU=All Users,DC=YOURDOMAIN,DC=COM" ;
$members = "CN=User1,OU=PathToAddGroupTo,OU=All Users,DC=YOURDOMAIN,DC=COM" ;
$members = "CN=User2,OU=PathToAddGroupTo,OU=All Users,DC=YOURDOMAIN,DC=COM" ;

Ldap_createGroup ($object_name , $dn , $members , $ldap_conn );
?>

The other function I created is ldap_bind(), and this can be used to bind to an LDAP server:

function ldap_bind ()
{
$ldap_addr = "192.168.1.1" ; // Change this to the IP address of the LDAP server
$ldap_conn = ldap_connect ($ldap_addr ) or die("Couldn"t connect!" );
ldap_set_option ($ldap_conn , LDAP_OPT_PROTOCOL_VERSION , 3 );
$ldap_rdn = "domain_name\\user_account" ;
$ldap_pass = "user_password" ;

// Authenticate the user against the domain controller
$flag_ldap = ldap_bind ($ldap_conn , $ldap_rdn , $ldap_pass );
return $ldap_conn ;
}
?>

13 years ago

When adding/editing attributes for a user, keep in mind that the "memberof" attribute is a special case. The memberOf attribute is not an accessible attribute of the user schema. To add someone to a group, you have to add the user in the group, and not the group in the user. You can do this by accessing the group attribute "member":

$group_name = "CN=MyGroup,OU=Groups,DC=example,DC=com" ;
$group_info [ "member" ] = $dn ; // User"s DN is added to group"s "member" array
ldap_mod_add ($connect , $group_name , $group_info );

?>

11 years ago

This solution works for us.
In the form the CN and pwdtxt are randomly generated from strict rules.
This script creates 50-60 users i AD pr.day! and never even had a glitch!

## From form
$CN = $_POST [ "CN" ];
$givenName = $_POST [ "givenName" ];
$SN = $_POST [ "SN" ];
$mail = $_POST [ "mail" ];
$Phone = $_POST [ "Phone" ];
$pwdtxt = $_POST [ "pwdtxt" ];

$AD_server = "localhost:390" ; // Local Stunnel --> http://www.stunnel.org/
$AD_Auth_User = "[email protected]" ; //Administrative user
$AD_Auth_PWD = "duppiduppdupp" ; //The password

$dn = "CN=" . $CN . ",OU=Brukere,DC=student,DC=somwhere,DC=com" ;

## Create Unicode password
$newPassword = "\"" . $pwdtxt . "\"" ;
$len = strlen ($newPassword );
$newPassw = "" ;

for($i = 0 ; $i < $len ; $i ++) {
$newPassw .= " { $newPassword { $i }} \000" ;
}

## CONNNECT TO AD
$ds = ldap_connect ($AD_server );
if ($ds ) {
ldap_set_option ($ds , LDAP_OPT_PROTOCOL_VERSION , 3 ); // IMPORTANT
$r = ldap_bind ($ds , $AD_Auth_User , $AD_Auth_PWD ); //BIND

$ldaprecord [ "cn" ] = $CN ;
$ldaprecord [ "givenName" ] = $givenName ;
$ldaprecord [ "sn" ] = $SN ;
$ldaprecord [ "objectclass" ][ 0 ] = "top" ;
$ldaprecord [ "objectclass" ][ 1 ] = "person" ;
$ldaprecord [ "objectclass" ][ 1 ] = "organizationalPerson" ;
$ldaprecord [ "objectclass" ][ 2 ] = "user" ;
$ldaprecord [ "mail" ] = $mail ;
$ldaprecord [ "telephoneNumber" ] = $Phone ;
$ldaprecord [ "unicodepwd" ] = $newPassw ;
$ldaprecord [ "sAMAccountName" ] = $CN ;
$ldaprecord [ "UserAccountControl" ] = "512" ;
//This is to prevent the user from beeing disabled. -->
http : //support.microsoft.com/default.aspx?scid=kb;en-us;305144

$r = ldap_add ($ds , $dn , $ldaprecord );

} else {
echo "cannot connect to LDAP server at $AD_server ." ;
}

?>

This is code example creates a user i AD.
We use this on an internal web page to create
temporary users that kan access the wireless network.
We have a .pl script that deletes the users after 24H.

11 years ago

Once i"am having problmes to add attributes with boolean syntax (1.3.6.1.4.1.1466.115.121.1.7)

$["boolean_attr"]=true; //give me one warning, ldap_add(): Add: Invalid syntax

solved this by setting the value on this:

$["boolean_attr"]="TRUE";

hope this can helps.

16 years ago

In response to jharnett"s question about accounts disabled by default from ldap_add, we have found a solution.

The attribute userAccountControl contains a value that includes whether the account is disabled or enabled. The default for us is 546; when we changed that to 544 the account became enabled. Changing whatever value is in userAccountControl by 2 seems to enable or disable the account.

The following code worked for us to create a new user with an enabled account:

$adduserAD["userAccountControl"] = "544";

We just added this element to the above example"s array.

7 months ago

Create Group in Active Directory

$ds = ldap_connect ("IP-server/localhost" );
$base_dn = "CN=Group name,OU=Organization Unit,DC=Domain-name,DC=com" ; //distinguishedName of group

If ($ds ) {
// bind with appropriate dn to give update access
ldap_bind ($ds , , "some-password" );

//Add members in group
$member_array = array();
$member_array [ 0 ] = "CN=Administrator,OU=Organization Unit,DC=Domain-name,DC=com" ;
$member_array [ 1 ] = "CN=User,OU=Organization Unit,DC=Domain-name,DC=com" ;

$entry [ "cn" ] = "GroupTest" ;
$entry [ "samaccountname" ] = "GroupTest" ;
$entry [ "objectClass" ] = "Group" ;
$entry [ "description" ] = "Group Test!!" ;
$entry [ "member" ] = $member_array ;
$entry [ "groupType" ] = "2" ; //GroupType="2" is Distribution / GroupType="1" is Security

Ldap_add ($ds , $base_dn , $entry );

Ldap_close ($ds );
} else {
echo "Unable to connect to LDAP server" ;
}
?>

14 years ago

Another fun thing: ldap_add() doesn"t like arrays with empty members: so
array (
= "name"
= ""
= "value"
will yield a syntax error!

solve this with a simple peice of code:

foreach ($originalobject as $key => $value){
if ($value != ""){
$object[$key] = $value;
}
}

where $originalobject is the uncecked array and $object is the one without empty members.

19 years ago

Ldap_add() will only honour the $entry["attribute"][x]="value" *if there are multiple values for the attribute*. If there is only one attribute value, it *MUST* be entered as $entry["attribute"]="value" or ldap_add() sets the value for the attribute to be "Array" instead of what you put into $entry["attribute"].

Here is a little routine I wrote up to do this automatically. when you"re parsing the input, just use multi_add():
function multi_add ($attribute , $value )
{
global $entry ; // the LDAP entry you"re gonna add

If(isset($entry [ $attribute ]))
if(is_array ($entry [ $attribute ]))
$entry [ $attribute ][ count ($entry [ $attribute ])] = $value ;
else
{
$tmp = $entry [ $attribute ];
unset($entry [ $attribute ]);
$entry [ $attribute ][ 0 ] = $tmp ;
$entry [ $attribute ][ 1 ] = $value ;
}
else
$entry [ $attribute ] = $value ;
}
?>
multi_add() checks to see if there is already a value for the attribute. if not, it adds it as $entry[$attribute]=$value. If there is already a value for the attribute, it converts the attribute to an array and adds the multiple values correctly.

How to use it:
switch($form_data_name )
{
case "phone" : multi_add ("telephoneNumber" , $form_data_value ); break;
case "fax" : multi_add ("facsimileTelephoneNumber" , $form_data_value ); break;
case "email" : multi_add ("mail" , $form_data_value ); break;
...
}
?>
In the system I designed the form has pulldowns with names ctype1, ctype2, ctype3, etc. and the values are "fax, mail, phone...". The actual contact data (phone number, fax, email, etc) is contact1, contact2, contact3, etc. The user pulls down what the contact type is (phone, email) and then enters the data (number, address, etc.)

I use variable variables to fill the entry and skip blanks. Makes for a very clean form entry system. email me if you"re interested in it, as I think I"m outgrowing the size of note allowed here. :-)

6 years ago

I kept getting "Object Class Violation" when I tried adding posixAccount and shadowAccount as an objectclass. It turned out that these object classes had a lot of required fields that I was not adding. You may need to export a working user (if you have phpLDAPadmin) and see exactly what fields they have, then try to copy it exactly in the script. It also doesn"t hurt if you make everything an Array the first time around, you can fix those fields later.

Drupal можно с легкостью назвать универсальным инструментом для реализации различных задач, также и определенной сложности enterprise решения.
Так получилось, что в основном Drupal я использую на предприятии, внутри Интранет сети, которая имеет Active Directory . И для того чтобы полноценно
использовать инфраструктура нашей сети, программой минимум это является Active Directory авторизация на сайтах и сервисах, и тут проблем у Drupal-а
нет, благодаря модулю Lightweight Directory Access Protocol (LDAP) .
В данном материале я хочу рассказать, как он настроен в моём окружении, и может кому то статья пригодится.

Итак, сразу забегая скажу, что с помощью данного модуля вы сможете предоставить:
1. LDAP авторизацию в вашей сети (в моём случае используется Active Directory от Microsoft)
2. Настроить сквозную авторизацию (когда пользователь автоматически входит под учётной записью операционной системы)
3. Ограничивать доступ, тем кто может входить на сайт и кому нельзя.
4. Синхронизация атрибутов учетной записи Active Directory с полями профиля пользователя на сайте, так и наоборот.
5. Актуализация учетной записи на сайте после смены логина учетной записи Active Directory (обычно происходит после смены фамилии сотрудника - меняется аналогично логин)
6. Присваивание роли пользователю на сайте в зависимости от его нахождения в определенной группе Active Directory

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

Скопируем к себе данные модуля, а также их зависимости Ctools и Entity API . На странице модулей активируем следующие модули:

  1. LDAP Authentication
  2. LDAP Authorization (модуль опционально, если вам требуется ограничение на доступ к сайта, либо ролям сайта по признаку нахождения пользователя в группах Active Directory)
  3. LDAP Authorization - Drupal Roles (дополнение к выше описанному модулю)
  4. LDAP Servers
  5. LDAP SSO (модуль опционально, если вам не требуется сквозная авторизация, либо пока веб-сервер не настроен под это, можно не устанавливать)
  6. LDAP User Module

Стоит отметить, что модуль не установится, если в ваших настройках PHP не установлен модуль php_ldap . Потому заранее установите, и настройте под характеристики вашего окружения.

После успешной установки модуля, перейдем в его настройки которые расположены здесь admin/config/people/ldap

Настройки модуля поделены на 4 вкладки: Settings , Servers , User , Authentication , Authorization

В первой вкладке настраивается метод шифрования паролей учетной записи Active Directory внутри Drupal-а.

На своей практике я это не использую, потому сразу перехожу во вторую вкладку Servers . И здесь мы остановимся по подробней.
Во вкладке отображены все созданные сервера LDAP авторизации, которые будут использованы на сайте, по умолчанию этот список пуст и нам с помощью кнопки Add LDAP Server Configuration можно создать новый сервер.
Страница создания сервера поделена на несколько блоков, рассмотрим каждый из них:

Connection settings

Machine name for this server configuration . - собственно машинное имя создаваемого сервера. Обычно называю active_directory
Name **- ещё какое то имя сервера, также называю active_directory
**Enabled
- ставлю галочку, таким образом включаю создаваемую конфигурацию сервера
LDAP Server Type - в моём случае выбираю Active Directory
LDAP server - доменное имя либо IP адрес по которому находится сервер Active directory, в моём случае ad.zv
LDAP port - отставляю по умолчанию 389 , у нас он такой же
Use Start-TLS - галочку не ставлю, т.к. не используем шифрование
Follow LDAP Referrals - также не ставлю, хотя так до конца и не разбирался для чего нужна данная настройка

Bingind method

Binding Method for Searches (such as finding user object or their group memberships)
Другими словами спрашивается каким образом, от чьего имени будет происходить подключение к сервере Active directory для поиска пользователя на факт существования, чтения его аттрибутов и т.д.
Также как и в описании к значения использую первый вариант Service Account Bind как best practice.
А именно подключение к серверу будет происходить от специальной заранее созданной учетной записи в Active directory которая имеет право на чтение каталогов и структуры Active Directory
Выбирая данный вариант ниже необходимо указать логин и пароль сервисной учетной записи в полях DN for non-anonymous search и Password for non-anonymous search

Clear existing password from database. Check this when switching away from Service Account Binding - пункт не применим к нашему методу описанному выше, потому галочку не ставим.

LDAP user to Drupal user relationship

Base DNs for LDAP users, groups, and other entries - базовый DN где находятся все пользователи в Active Directory, в моём случае ставлю DC=ad,DC=zv . В данной настройке лучше проконсультироваться с системными администраторами вашего сервера Active Directory
AuthName attribute и AccountName attribute - атрибут в котором хранится логин пользователя и аккаунт имя, обычно они должны быть одинаковы, и в большинстве случаев указывается samaccountname , т.к. по умолчанию логин находится там в Active Directory
Email attribute - атрибут в котором находится почтовый ящик пользователя, в моём случае mail . Стоит отметить что для Drupal-а это поле обязательно, т.к. Drupal не может иметь пользователя без почтового ящика, потому если в вашем окружении есть вероятность того что существует пользователи без почтового ящика, то вам необходимо воспользоваться полем ниже для заполнения почтового ящика по шаблону, либо позже в другой вкладке мы вернёмся к данной настройке с другой стороны.
Email template - шаблон почтового ящик, применяется тогда, когда ваши почтовые ящики например идентичны логину, либо состоят из нескольких атрибутов пользователя в Active Directory. Потому вы может просто составить его из токенов аттрибутов.
Thumbnail attribute - атрибут в котором находится изображение пользователя (в binary) для последующей загрузки в изображения профиль пользователя в Drupal-е, в моём случае thumbnailPhoto
Persistent and Unique User ID Attribute - уникальный атрибут, который никогда не изменится у пользователя Active Directrory. Применяется в тех случаях когда например логин у вас представляет собой фамилию и инициалы пользователя, и если вдруг пользователь сменил фамилию, и следом ему изменили логин, то для следующего входа им на сайт для Drupal-а он будет по умолчанию как новый пользователь, и Drupal будет создавать новую учётную запись (конечно при условии, что почтовый ящик ему тоже сменили, т.к. если он останется прежним, то Drupal сообщит о конфликте почтового ящика, и не создаст новую запись). Именно поэтому данное поле служит уникальным ключом на которое в первую очередь будет смотреться при входе пользователя на сайт, даже если он сменил логин, Drupal сначала найдёт его по данному атрибуты, и тогда по нему построит связь с пользователем Active Directory и как следствие обновит ему логин на сайте Drupal. В моем случае это objectsid
Does the Persistent and Unique User ID Attribute hold a binary value? - галочку ставлю, т.к. objectsid хранится в binary.

Эта статья также доступна на следующих языках: Тайский

  • Next

    Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

    • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

      • Next

        В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

  • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
    https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png