Создаём WEB адресный справочник PHP + LDAP

Так случилось, что у (относительно) большой кампании было много отдалённых офисов, в которых находилось приличное количество пользователей. Все офисы соединены в одну сеть с общим доменом, каждый офис был определён в Active Directory (далее по тексту AD) как Organization Unit (OU), в котором уже заводились пользователи.

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


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

Начнём с того, что для начала нужно определиться что использовать, это просто — итоговый справочник должен быть доступен всем пользователям домена посредством браузера. Первое, что приходит на ум это PHP в связке с ldap, их и будем использовать. Большим плюсом использования PHP я считаю его относительную простоту — любой хоть немного понимающий системный администратор сможет внести, при необходимости, нужные правки в код, не особенно напрягаясь.

Итак, начнём. для начала зададим параметры подключения к домену:

$srv ="SERVER";
$srv_domain ="DOMAIN.COM";
$srv_login ="USERNAME@".$srv_domain; 
$srv_password ="PASSWORD";

Следующим пунктом нужно определить в каком OU будем искать пользователей. Делать это будем перехватывая значения из $_GET['place']. Например, если пользователь переходит по адресу server/index.php?place=first, то переменной $place будет присвоено значение first.

$place = (@$_GET['place']);
$doscript=true;
switch($place){ 
case "first" :
	$dn ="OU=ou1,OU=DOMAIN,dc=DOMAIN,dc=COM";			
	break;
case "second":
	$dn ="OU=ou2,OU=DOMAIN,dc=DOMAIN,dc=COM";			
	break;
	//здесь можно добавить ещё условий.
default:
	$doscript=false; 
	break;
}
if (!$doscript) include "main_table.html";

Переменная $doscript нужна для того, чтобы хранить значение — определили ли мы OU, в котором будем искать пользователей или нет. Если не нашлось совпадений, перечисленных в «switch-case», то $doscript=false, главная часть скрипта выполняться не будет, а будет выведена стартовая страница «main_table.html» (о ней расскажу в самом конце).

Если же мы определили OU, тогда приступаем к дальнейшим действиям: начинаем рисовать пользователю страницу справочника:

else if ($doscript) {
{echo "
<!DOCTYPE html> 
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<link rel='shortcut icon' href='ico.png'>
<meta charset='windows-1251/ '>

Включаем стили для более приятного внешнего вида (да, их можно было подключить как css-файл, однако некоторые версии IE не хотят воспринимать стили, заданные таким образом, поэтому приходится писать прямо в скрипт):

<style>
	*{text-align: center; font-family:tahoma; font-size:14px;}
	a{text-decoration: none; color: #000;}
	a:hover{text-decoration: underline; color: #0059FF;}
	#bold{text-decoration: none; font-weight: 600;font-size:20px;}
	#table,tr,td{border-style:solid;border-width:1px;	border-collapse:collapse;padding:5px; height:22px;border-color:#7d7d7d;}
	/* Нечетные строки */#table tbody tr:nth-child(odd){background: #fff;}
	/* Четные строки */   #table tbody tr:nth-child(even){background: #F7F7F7;}	
	#noborder{border-width: 0 px; border-style: none;}	
	#sp30px{text-indent: 30px;text-align: justify;}
	#smallsize{font-family:tahoma; text-indent: 5px; text-align:left; font-size:12px;}
	#top {background: #ffffff;
		text-align: center;
		left:0;
		top:0px;
		table-layout: fixed;
		border-style:solid;
		border-width:0px;
		border-collapse:collapse;
		padding:0px;
		height:22px;
		border: 0px;
		z-index: 99999;
		display:block;
		width:80px;
		opacity: 0.6;
		filter: alpha(Opacity=60);
		height:100%;
		position:fixed;}
	#top:hover{background: #afafaf;opacity: 100;filter: alpha(Opacity=100);text-decoration: none;color: #000000;}
	.smalltext{padding-top: 1px;
		padding-bottom: 1px;
		text-align: bottom;
		font-family:tahoma;
		color: #a0a0a0;
		line-height: 7px;
		font-size: 10px;}
	.smalltext:hover{color: #0000ff;}		
	.transition-rotate {position: relative;
		z-index: 2;
		margin: 0 auto;
		padding: 5px;
		text-align: center;
		max-width: 500px;
		cursor: pointer;
		transition: 0.1s linear;}
	.transition-rotate:hover {-webkit-transform: rotate(-2deg);	transform: rotate(-2deg);}
	#lineheight{
		text-align: left;
		line-height: 1px;
		text-decoration: none;
		font-weight: 600;
		font-size:20px;}
</style>

Со стилями покончено, теперь пишем заголовок вкладки и рисуем удобную ссылку возврата на главную страницу:

<title>Adressbook of «YourMegaCompanyName»</title>	
</head>
<body style='background-color:#ffffff;'>";
}
echo "
<table id='top'><tr><td id='top'>
<a href='index.php?place=main' id='top' >
<br><br><br>
<img src='back_to_main.png' alt='' border='0' width='75' height='60'/>
<p>На главную</p></a>
</td></tr></table>
";

Определяем поисковые фильтры по AD, и получаем данные об OU:

$filter ="(&(objectcategory=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"; //все пользователи, кроме отключенных.
$filter2 ="(objectCategory=OrganizationalUnit)"; // для получения информации о OU
$ds=ldap_connect($srv);   
if ($ds) { 
    $r=ldap_bind($ds,$srv_login,$srv_password);;     
	ldap_set_option($ds,LDAP_OPT_REFERRALS, 0);
	ldap_set_option($ds,LDAP_OPT_PROTOCOL_VERSION,3);
	$sr=ldap_search($ds,$dn ,$filter );   
    ldap_sort($ds,$sr, "givenname");
    $info = ldap_get_entries($ds, $sr); 
    $sr2=ldap_search($ds,$dn ,$filter2 );   
    $placeinfo = ldap_get_entries($ds, $sr2); 
$PlaceName = $placeinfo[0]["l"][0];  			// name of place
$PlaceAddres = $placeinfo[0]["street"][0];		// address of place
$PlaceMail = $placeinfo[0]["description"][0]; 	// mail of place
$PlacePhone = $placeinfo[0]["st"][0]; 		// phone of plase

Далее оформляем верхнюю часть страницы:

echo"<table align='center' height = '80'>
	<td id='noborder' ><div id='lineheight'>". $PlaceName ."</div></td></tr>
	<tr><td id='noborder' >". $PlaceAddres ."</td></tr>
    </table>
<table align='center' id='table'>
	<tr><td width='35' bgcolor = #f0f0e4>  № </td>
	<td width='300' bgcolor = #f0f0e4> Name </td>
	<td width='250' bgcolor = #f0f0e4> E-mail </td>
	<td width='60' bgcolor = #f0f0e4> Phone </td>
	<td width='150' bgcolor = #f0f0e4> Mobile </td></tr>
	<tr><td></td><td> Данные OU </td><td>";
echo "<div class='transition-rotate'><a href=mailto:" . $PlaceMail .">" . $PlaceMail ." </a></div>";
echo "</td><td width='150'> " . $PlacePhone ." </td><td> - </td></tr>";

Далее получаем в цикле и обрабатываем данные пользователей, при этом, чтобы скрыть некоторые (например служебные) учётные записи, просто прописываем «hide» в поле «комната» в реквизитах пользователя в AD, такие пользователи не будут отображаться в справочнике:

for ($i=0; $i<$info["count"];$i++) { 
$UserHide = $info[$i]["physicaldeliveryofficename"][0];
if ($UserHide != 'hide') {
$UserName = $info[$i]["cn"][0];                //Имя пользователя
$UserPosition = $info[$i]["title"][0]; 		// Должность
$UserMail = $info[$i]["mail"][0];			//mail
if (!$UserMail)) $UserMail = "-";                  //если нет данных о ящике в AD, то отображаем прочерк
$UserIpPhone = $info[$i]["ipphone"][0];		//ip phone
	if (!$UserIpPhone) $UserIpPhone = "-";    //если нет данных о ящике в AD, то отображаем прочерк
$UserMobile = $info[$i]["mobile"][0];		//mobile
	if (!$UserMobile) $UserMobile = "-";     //если нет данных о ящике в AD, то отображаем прочерк

Кстати, если вам потребуется получить значение другого атрибута, то помните (это важно):
в запросе передаём имя атрибута строчными буквами, иначе не заработает.

И вставляем полученные данные в таблицу:

    echo "<tr>
	<td>". $n+=1 ."</td>
	<td> ". $UserName ."<br> <div class='smalltext'>". $UserPosition ."</div></td><td>"; //	Имя пользователя и должность 
	if ($UserMail !='-') echo "<div class='transition-rotate'><a href=mailto:'$UserMail'>$UserMail  </a></div>";    // если у пользователя есть e-mail создаём ссылку на отправку письма
	else echo "-"; //если нет e-mail - ставим прочерк.
 	echo "<td> ". $UserIpPhone ." </td>
 	<td> ". $UserMobile ." </td></tr>";
	}
}
echo "</table>";

Далее мы закрываем подключение по ldap, или выводим сообщение о невозможности подключения к серверу:

ldap_close($ds); 
} 
else echo "<h4>Unable to connect to LDAP server</h4>"; 
echo '<br><br><br></body></html>';}

Файл «main_table.html» изнутри себя представляет простую html страницу с ссылками, и выглядит примерно так:

<head>
<link rel="shortcut icon" href="ico.png"/>
<meta charset="windows-1251"/>
<title>Adressbook of «YourMegaCompanyName»</title>
</head>
<body style='background-color:#ffffff;'>
<center><a href=index.php><IMG border="none" src="logo.png"/></a></center>
<center><b>Places and offices</b></center>
<br>
<table border="0" width="450" bgcolor="#dddddd" align="center" valign="middle" CELLSPACING="0">

<tr id="space"><td></td></tr>
<tr><td align="left" id="abz"><a href="index.php?place=ou1">OU1</a></td></tr>
<tr id="space"><td></td></tr>
<tr><td align="left" id="abz"><a href="index.php?place=ou2">OU2</a></td></tr>

</table></body></html>

Если кому-либо поможет мой код — я буду рад, пользуйтесь!

Также можете свободно редактировать его как вам угодно (улучшать/ухудшать) и распространять любыми методами.

Спасибо за внимание!

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 19

    +6

    Вот из-за подобных статей у людей складывается впечатление, что PHP — хреновый ЯП.

      0
      Не совсем понял, почему вы так решили?
      На самом деле не существует «плохих» или «хороших» языков программирования — просто каждый хорош на своём месте и в своё время (это наподобие как вилкой гвозди забивать, молотком макарошки есть, а потом утверждать, что и вилка и молоток никуда не годятся).
      А если кто-либо не понимает этого, то это лично его проблемы и ему с этим жить. Оставим такого человека — пусть идёт с миром.
        +1
        Потому что код плохой (уж извините, но как есть). Некоторые могут решить, что дело не в применении, а в языке, как результат — негативное впечатление ко второму.

        Плохой, потому что, не отформатирован, не последователен и всё свалено в кучу. Хоть кода совсем немного, но всё равно стоило бы разделить маршрутизацию, обработчики и шаблонизацию.
      +2
      С одной стороны — может быть и будет полезно в очень узких кругах, с другой — качество кода оставляет желать лучшего.

      Единый стиль именования переменных? Неее, это было бы слишком просто.
      $UserMobile
      $placeinfo
      $srv_domain 
      


      Пробелы закончились?
      ($i=0; $i<$info["count"];$i++)
      


      в запросе передаём имя атрибута строчными буквами, иначе не заработает.

      А с чем связано такое ограничение? Или что-то мешает через функцию strtolower пропустить имя атрибута?

      Почитайте PSR-2, а затем PSR-12 — это пойдёт на пользу.
        +1
        Вы вроде бы советуете, а человек потом нарвется еще на проблемы.
        mb_strtolower же, вдруг решит русские символы передать.
          +1
          Вы правы.
          +1
          Согласен с вам полностью — код далеко не безупречный (но я старался).
          в запросе передаём имя атрибута строчными буквами, иначе не заработает.

          А с чем связано такое ограничение? Или что-то мешает через функцию strtolower пропустить имя атрибута?

          Данное ограничение связано с особенностями работы php-ldap.
          И зачем пропускать через функцию strtolower? Параметр захардкожен и не меняется.

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

          Кстати, спасибо за замечания — учту и исправлюсь.
          (я бы проголосовал за ваш комментарий, но у меня недостаточно кармы для этого)
          0
          Долго искали решение, т.к. когда появляется 50+ сотрудников, листик с номерами становится очень тяжко обновлять.
          Вдохновились Telephone Directory хабраюзера titulusdesiderio, немного допилили под себя и стало огонь. Основной плюс его решения — поиск (ФИО, отдел, тел, мыло, должность) на лету, без перезагрузки страницы, и сортировки, хочешь по ФИО, хочешь по отделам.
            0
            Интересно.
            Спасибо, почитаю.
            0
            Что то я не понимаю. В относительно большой компании справочником обычно является Outlook.
              0

              Outlook очень неудобен для телефонного справочника. Гораздо удобнее так, как автор написал. У самого так сделано. И нужную информацию всегда можно наглядно получить.

                0
                Работая в «относительно» большой компании, «протоплю» за кастомные решения…
                Outlook откровенно хуже.
                0

                У нас вот этот справочник используется: https://habr.com/ru/post/328560/
                Поиск не очень хорошо работает, но зато есть карта. Новым сотрудникам удобно ориентироваться.

                  0

                  Тоже на каждом месте работы делал телефонный справочник с учетом структуры организации. Тоже пытался работать с ldap и в конце концов плюнул и запихнул все в аксессную базу. Там в ней же у меня хранится весь телефонный кросс, кроссировка от АТС, база мобильных, штатное расписание и тд. Вобщем посредством правильных запросов могу получить 5сто душа пожелает ну и базу модифицировать под текущие нужды. Опять же сам аксесс упрощает работу с базой в плане правок. Впринципе можно конечно сделать увязку моей базы с ldap ad но пока както и так все хорошо справляется.

                    0
                    Хорошо тем, у кого сотрудники всегда в одном кабинете работают. А если нужен телефонный справочник людей и кабинетов. Сначала мы создадим кучу контактов вида кабинет-телефон, а потом немного магии…
                      0

                      А в чем проблема выдернуть номер кабинета их той же АД? У меня все это храниться в той же таблице базы где и привязка номеров АТС.

                        0
                        Проблема в том, что сотрудники переезжают сами по себе, иногда забирают все оборудование включая телефон. И ведение любого такого справочника превращается в боль.
                      0

                      Это у вас какая то анархия в конторе и проблема чисто организационного характера. У нас в конторе сотрудник если и поедет сам все равно обратится чтобы ему переставить комп.
                      Потом не знаю как у вас а у нас АТС аналоговая, и соответственно все равно обратится чтобы перекроссировать.

                        +1
                        Автор молодец, а тот кто критикует пусть сразу ссылку выкладывает как сделал он и тогда хоть можно будет предметно о чем-то говорить. А огульно критиковать любой сможет — то не так, это не то ну покажи тогда что-то лучшее раз ты такой умный!!!

                        Only users with full accounts can post comments. Log in, please.