Выводим список контактов организации из Active Directory на сайт компании
Простенькая задача: есть компания, внутри которой развернута служба каталогов Active Directory от Microsoft. Нам хочется — что при открытии в браузере ссылки contacts.mycompany.local — выводился список сотрудников компании с разбивкой по отделам.
Предполагается, что в компании у каждого сотрудника есть своя учетная запись в Active Directory, у которой скудно-бедно заполнены хотя бы основные атрибуты, такие ФИО сотрудника, адрес его электронной почты, номер телефона, должность, отдел. Имя домена нашей компании — mycomany.local.
Для примера, будет выбран такой макет компании:

Итак, поехали. Первым делам создаем в Active Directory учетную запись, под которой будем подключаться к службе каталогов для получения информации о сотрудниках.
Запускаем оснастку «Active Directory — пользователи и компьютеры» и добавляем пользователя ldap_reader, устанавливаем ему пароль ldap_reader:

Именно под этим пользователем мы будем работать с каталогом LDAP.
Даем этому пользователю права на чтение подразделения «Пользователи». Для это нажимай по «Пользователи» правой кнопкой, выбираем вкладку безопасность и добавляем туда нашего товарища «LDAP Reader», попутно убеждаясь — что он имеет права только на чтение:

На этом прощаемся с семейством Windows и переключаемся на наш WWW-сервер.
WWW-сервер у нас работает под FreeBSD, установлены фреймворкdjango и легковесный HTTP-сервер lighttpd .
Устанавливаем из портов py-ldap:
После этого python умеет с протоколом LDAP.
Создаем каталог, где будет жить наш проект djanho:
И создаем наш проект:
Создаем в нашем проекте приложение app_contacts:
Создаем каталог, где будет лежать БД этого проекта:
Редактируем файл конфигурации проекта:
И создаем БД проекта:
И пишем обработчик fastcgi:
Теперь переходим к настройке lighhtpd, вносим следующие изменения:
ВАЖНО! Обработчик fastcgi будет запущен от того-же пользователя, что и lighttpd. При работе django — изменяется файл БД проекта, поэтому у процесса должны быть права на запись в файл БД. А так как используется sqlite3 — то еще и права на запись в каталог с файлом с БД.
Lighttpd у нас запущен от пользователя www, поэтому даем соответствующие права на БД:
Проверяем что получилось. Перезапускаем lighttpd и переходим в браузере по ссылкиcontacts.mycompany.local :

Все нормально, django отчитался о готовности к работе.
Создаем обертку, для обработки запросов к LDAP-серверу:
Заодно приводим файл /var/django-projects/contacts/app_contacts/views.py к виду:
Создаем шаблон html-странцы:
И добавляем обработку ссылок вида «department/(.*)$ » нашим приложением, редактируем файл urls.py:
Так же включаем наш каталог с шаблонами в список каталогов шаблонов. Для этого отредактируем строки в файле setting.py:
Перезапускаем lighttpd и смотрим что получилось:



Вроде все работает, а значит — с чистой совестью можно пойти на перекур :)
Предполагается, что в компании у каждого сотрудника есть своя учетная запись в Active Directory, у которой скудно-бедно заполнены хотя бы основные атрибуты, такие ФИО сотрудника, адрес его электронной почты, номер телефона, должность, отдел. Имя домена нашей компании — mycomany.local.
Для примера, будет выбран такой макет компании:

Итак, поехали. Первым делам создаем в Active Directory учетную запись, под которой будем подключаться к службе каталогов для получения информации о сотрудниках.
Запускаем оснастку «Active Directory — пользователи и компьютеры» и добавляем пользователя ldap_reader, устанавливаем ему пароль ldap_reader:

Именно под этим пользователем мы будем работать с каталогом LDAP.
Даем этому пользователю права на чтение подразделения «Пользователи». Для это нажимай по «Пользователи» правой кнопкой, выбираем вкладку безопасность и добавляем туда нашего товарища «LDAP Reader», попутно убеждаясь — что он имеет права только на чтение:

На этом прощаемся с семейством Windows и переключаемся на наш WWW-сервер.
WWW-сервер у нас работает под FreeBSD, установлены фреймворк
Устанавливаем из портов py-ldap:
[dk@www /usr/ports/net/py-ldap2]$
[dk@www /usr/ports/net/py-ldap2]$ sudo make && make install && make cleandir
После этого python умеет с протоколом LDAP.
Создаем каталог, где будет жить наш проект djanho:
[dk@www ~]$ sudo bash
Password:
[root@www /usr/home/dk]# mkdir -p /var/django-projects/
И создаем наш проект:
[root@www /usr/home/dk]# cd /var/django-projects/
[root@www /var/django-projects]# django-admin.py startproject contacts
[root@www /var/django-projects]# ls
contacts
[root@www /var/django-projects]#
[root@www /var/django-projects]# cd contacts/
[root@www /var/django-projects/contacts]# ls
__init__.py manage.py settings.py urls.py
[root@www /var/django-projects/contacts]# chmod +x manage.py
[root@www /var/django-projects/contacts]# ls -l
total 14
-rw-r--r-- 1 root wheel 0 9 май 17:01 __init__.py
-rwxr-xr-x 1 root wheel 546 9 май 17:01 manage.py
-rw-r--r-- 1 root wheel 2777 9 май 17:01 settings.py
-rw-r--r-- 1 root wheel 546 9 май 17:01 urls.py
[root@www /var/django-projects/contacts]#
Создаем в нашем проекте приложение app_contacts:
[root@www /var/django-projects/contacts]# django-admin.py startapp app_contacts
[root@www /var/django-projects/contacts]# ls
__init__.py __init__.pyc app_contacts manage.py settings.py settings.pyc urls.py
[root@www /var/django-projects/contacts]#
Создаем каталог, где будет лежать БД этого проекта:
[root@www /var/django-projects/contacts]# mkdir -p /var/django-projects/contacts/db
[root@www /var/django-projects/contacts]#
Редактируем файл конфигурации проекта:
[root@www /var/django-projects/contacts]# cat settings.py
# Django settings for contacts project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = '/var/django-projects/contacts/db/project.db' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'Europe/Moscow'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'ru-ru'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'v1@h8^n#-$y9+975^np#=ho3a3q^rh#6nt!#vc$89=-q_2+*nw'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'contacts.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'app_contacts',
)
И создаем БД проекта:
[root@www /var/django-projects/contacts]# ./manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'root'):
E-mail address: root@mycompany.local
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
И пишем обработчик fastcgi:
[root@www /var/django-projects/contacts]# cat contacts.fcgi
#!/usr/local/bin/python
import sys, os
sys.path.insert(0, '/var/django-projects')
os.chdir('/var/django-projects/contacts')
os.environ['DJANGO_SETTINGS_MODULE'] = 'contacts.settings'
from django.core.servers.fastcgi import runfastcgi
runfastcgi(method='threaded', daemonize='false')
Теперь переходим к настройке lighhtpd, вносим следующие изменения:
# contacts.mycompany.local
$HTTP["host"] == "contacts.mycompany.local" {
url.rewrite-once = (
"^(/media.*)$" => "$1",
"^(/admin/media.*)$" => "$1",
"^/favicon\.ico$" => "/media/favicon.ico",
"^(.*)$" => "/fcgi$1"
)
server.document-root = "/var/django-projects/contacts"
fastcgi.server = ( "/fcgi" => ( "localhost" =>
(
"socket" => "/tmp/contacts-fastcgi.sock",
"check-local" => "disable",
"bin-path" => "/var/django-projects/contacts/contacts.fcgi",
)
))
}
ВАЖНО! Обработчик fastcgi будет запущен от того-же пользователя, что и lighttpd. При работе django — изменяется файл БД проекта, поэтому у процесса должны быть права на запись в файл БД. А так как используется sqlite3 — то еще и права на запись в каталог с файлом с БД.
Lighttpd у нас запущен от пользователя www, поэтому даем соответствующие права на БД:
[root@www /var/django-projects/contacts]# chown -R www /var/django-projects/contacts/db
[root@www /var/django-projects/contacts]#
Проверяем что получилось. Перезапускаем lighttpd и переходим в браузере по ссылки
[root@www /var/django-projects/contacts]# /usr/local/etc/rc.d/lighttpd restart
Performing sanity check on lighttpd configuration:
Syntax OK
Stopping lighttpd.
Waiting for PIDS: 40773.
Starting lighttpd.
[root@www /var/django-projects/contacts]#

Все нормально, django отчитался о готовности к работе.
Создаем обертку, для обработки запросов к LDAP-серверу:
[root@www /var/django-projects/contacts/app_contacts]# cat common.py
# -*- coding: utf-8 -*-
import ldap
import ldap.filter
# Подключение к LDAP-серверу
def ldap_connect():
server = 'dc.mycompany.local'
user = 'ldap_reader@mycompany.local'
password = 'ldap_reader'
l = ldap.open(server)
l.simple_bind_s(user, password)
return l
# Получить значение из entry
def ldap_getEntryValue(entry, key, default = None):
if key in entry:
return entry[key][0]
else:
return default
# Вывод информации по сотрудникам, принадлежащих отделу department
def ldap_getDepartment(department):
# Подключаемся к LDAP
connect = ldap_connect()
# Фильтр
filter = '(&(name=*)(department={0}))'.format(ldap.filter.escape_filter_chars(department))
# Отсылаем запрос
search = connect.search_s(
'dc=mycompany,dc=local',
ldap.SCOPE_SUBTREE,
filter,
[ 'name', 'mail', 'title', 'telephoneNumber' ]
)
# Формируем результат поиска
result = [ ]
for dn, entry in search:
if dn and entry:
result.append( {
'name': ldap_getEntryValue(entry, 'name'),
'mail': ldap_getEntryValue(entry, 'mail'),
'title': ldap_getEntryValue(entry, 'title', '-'),
'phone': ldap_getEntryValue(entry, 'telephoneNumber', '-'),
} )
# Возвращаем результат поиска
return result
Заодно приводим файл /var/django-projects/contacts/app_contacts/views.py к виду:
[root@www /var/django-projects/contacts/app_contacts]# cat /var/django-projects/contacts/app_contacts/views.py
# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from common import *
# Список отделов
departments = [ 'Начальство', 'Отдел продаж', 'Отдел сбыта' ]
def department(request, name):
context = RequestContext(request)
context['contacts'] = ldap_getDepartment(name.encode('utf-8'))
context['departments'] = departments
context['department'] = name
return render_to_response('app_contacts/contacts.html', context)
Создаем шаблон html-странцы:
[root@www /var/django-projects/contacts/app_contacts]# cat /var/django-projects/contacts/templates/app_contacts/contacts.html
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html lang="ru">
<head>
<title>Контакты MyCompany: {{ department }}</title>
<meta name="keywords" content="">
<meta name="description" content="">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
<center><h2>Контакты MyCompany: {{ department }}</h2></center>
<center><h4>Отделы</h4></center>
<center>
{% for d in departments %}
<a href="/department/{{ d }}">{{ d }}</a>
{% endfor %}
</center>
<table style="border: 1px double black;" align="center">
<tr >
<th>ФИО</th>
<th>Должность</th>
<th>Эл. почта</th>
<th>Телефон</th>
</tr>
{% for c in contacts %}
<tr>
<td style="border: 1px double black; padding: 2px;">{{ c.name }}</td>
<td style="border: 1px double black; padding: 2px;">{{ c.title }}</td>
<td style="border: 1px double black; padding: 2px;"><a href="mailto:{{ c.mail }}">{{ c.mail }}</a></td>
<td style="border: 1px double black; padding: 2px;">{{ c.phone }}</td>
</tr>
{% endfor %}
</table>
И добавляем обработку ссылок вида «department/(.*)$ » нашим приложением, редактируем файл urls.py:
...
urlpatterns = patterns('',
(r'^department/(.*)$', 'app_contacts.views.department'),
)
...
Так же включаем наш каталог с шаблонами в список каталогов шаблонов. Для этого отредактируем строки в файле setting.py:
...
TEMPLATE_DIRS = (
'/var/django-projects/contacts/templates',
)
...
Перезапускаем lighttpd и смотрим что получилось:
[root@www /var/django-projects/contacts]# /usr/local/etc/rc.d/lighttpd restart
Performing sanity check on lighttpd configuration:
Syntax OK
Stopping lighttpd.
Waiting for PIDS: 41689.
Starting lighttpd.
[root@www /var/django-projects/contacts]#



Вроде все работает, а значит — с чистой совестью можно пойти на перекур :)
3 комментария