Python ldap get user

/привет/мир/etc

Недавно я написал о том, что такое LDAP и с чем его едят. Сегодня, в продолжение этой темы, расскажу, как работать с LDAP сервером из программы на языке программирования Python. Для создания примеров я использую Python 2.7 и MS Active Directory. В примерах все имена организаций, подразделений и пользователей являются вымышленными, а всякое совпадение с реальностью является совершенно случайным 🙂

Для работы с LDAP из Python нам понадобится пакет python-ldap. Пакет позволяят работать с различными LDAP-серверами, и с Active Directory, в частности.

>>> import ldap >>> ad = ldap.initialize("ldap://192.168.0.16") >>> ad.simple_bind_s("trofimov_a@SKY", "nooneknows") (97, [], 1, []) 

По LDAP URL ldap://192.168.0.16 , который указывает на Active Directory сервер, получен объект LDAPObject и присвоен переменной ad . Далее используем метод simple_bind_s этого объекта для присоединения к серверу, передавая имя и пароль пользователя для аутентификации. (Символы _s в конце имени метода означают, что метод выполняется синхронно.)

Аутентификация пользователей — одна из функций LDAP сервера, реализуемая операцией bind. Другие функции: поиск, чтение и модификация данных в каталоге.

Найдем и выведем на экран атрибут name всех подразделений ( objectClass=organizationalUnits ) верхнего уровня ( ldap.SCOPE_ONELEVEL ) для организации «Синее Небо» ( O=Синее Небо,DC=org,DC=ru ):

>>> basedn = 'O=Синее Небо,DC=org,DC=ru' >>> scope = ldap.SCOPE_ONELEVEL >>> filterexp = 'objectClass=organizationalUnit' >>> attrlist = ['name'] >>> results = ad.search_s(basedn, scope, filterexp, attrlist) >>> for result in results: . print result[0].decode('utf-8'), result[1]['name'][0].decode('utf-8') . OU=Коммерческий отдел,O=Синее Небо,DC=org,DC=ru Коммерческий отдел OU=Общее руководство,O=Синее Небо,DC=org,DC=ru Общее руководство OU=Отдел обеспечения,O=Синее Небо,DC=org,DC=ru Отдел обеспечения OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru Производственный отдел 

Метод search_s возвращает результат поиска как список кортежей (DN записи, словарь с запрошенными атрибутами). А поскольку атрибут может иметь больше одного значения, то значения атрибутов представлены списком. В рассмотренном примере атрибут name имеет одно значение, то есть, список значений содержит единственный элемент result[1][‘name’][0] .

Читайте также:  Add php to iframe

Значения, которые может принимать параметр scope (в соответствии со спецификацией LDAP):

  • ldap.SCOPE_BASE — запись base DN,
  • ldap.SCOPE_ONELEVEL — дочерние записи base DN,
  • ldap.SCOPE_SUBTREE — поддерево с вершиной base DN.

Найду запись о пользователе (objectClass=user) , указав в списке атрибутов objectClass , — этот атрибут имеет несколько значений:

>>> basedn = 'O=Синее Небо,DC=org,DC=ru' >>> scope = ldap.SCOPE_SUBTREE >>> filterexp = "(&(cn=Трофимов Андрей*)(objectClass=user))" >>> attrlist = ["sAMAccountName", "mail", "objectClass"] >>> results = ad.search_s(basedn, scope, filterexp, attrlist) >>> for result in results: . print 'DN'.rjust(15) + ' = ' + result[0].decode('utf-8') . print '\n'.join(x.rjust(15) + ' = ' + str(result[1][x]) for x in attrlist) . DN = CN=Трофимов Андрей,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru sAMAccountName = ['trofimov_a'] mail = ['trofimov_a@sineenebo.org.ru'] objectClass = ['top', 'person', 'organizationalPerson', 'user'] 

Несколько значений атрибута objectClass отражают тот факт, что класс user явлется наследником классов organizationalPerson , person и top . Таким образом, запись класса user является одновременно записью каждого из классов-предков.

Атрибут sAMAccountName содержит имя пользователя, под которым пользователь регистрируется в корпоративном домене, атрибут mail — адрес электронной почты.

Следующий скрипт выводит на экран имя и адрес электронной почты всех активных пользователей организации «Синее Небо». Приблизительно то же самое делает адресная книга вашего почтового клиента, чтобы вывести доступных адресатов электронной почты.

# -*- coding: utf-8 -*- import ldap LDAP_URL = "ldap://192.168.0.16" USERNAME = "trofimov_a@SKY" PASSWORD = "nooneknows" ad = ldap.initialize(LDAP_URL) ad.simple_bind_s(USERNAME, PASSWORD) basedn = "O=Синее Небо,DC=org,DC=ru" scope = ldap.SCOPE_SUBTREE filterexp = "(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(cn=*)(mail=*))" attrlist = ["cn", "mail"] results = ad.search_s(basedn, scope, filterexp, attrlist) for result in results: print result[1]["cn"][0].decode("utf-8").rjust(35), result[1]["mail"][0] ad.unbind_s() 

Здесь в выражении фильтра расширенная проверка с атрибутом userAccountControl выбирает только пользователей с отключенной учетной записью ( AccountDisabled ), а ее отрицание, соответственно, выбирает активных пользователей. Выражение фильтра также требует наличия у записей атрибутов cn и mail .

В отличие от операции поиска, операции LDAP для изменения данных всегда выполняются ровно для одной записи в каталоге, идентифицированной ее уникальным именем DN.

Добавим новую запись класса organizationalPerson в Производственный отдел:

>>> dn = "CN=Обломов Илья,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru" >>> addlist = [ . ("cn", "Обломов Илья"), . ("sn", "Обломов"), . ("objectClass", ["organizationalPerson","person","top"]), . ("telephoneNumber", "2121212") . ] >>> ad.add_s(dn, addlist) (105, []) 

Метод add_s объекта LDAPObject принимает в качестве параметров DN новой записи и список кортежей (атрибут, значение). Запись успешно добавлена. Для чтения записи создам функцию:

>>> def show_entry(dn): . try: . results = ad.search_s(dn, ldap.SCOPE_BASE) . print "DN".rjust(20), "=", results[0][0].decode("utf-8") . for attr in ["cn", "sn", "givenName", "telephoneNumber"]: . print attr.rjust(20), "=", (results[0][1][attr][0].decode("utf-8") if results[0][1].get(attr) else '') . except: . print ("Не найден DN %s" % dn).decode("utf-8") . >>> show_entry(dn) DN = CN=Обломов Илья,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru cn = Обломов Илья sn = Обломов givenName = telephoneNumber = 2121212 

Для изменения существующей записи нужно подготовить список модификаций:

>>> modlist = [ . (ldap.MOD_ADD, "givenName", "Илья"), . (ldap.MOD_REPLACE, "sn", "Обломоff"), . (ldap.MOD_DELETE, "telephoneNumber", None) . ] 

Первый элемент каждого кортежа в списке определяет тип модификации, одно из:

  • ldap.MOD_ADD — добавить атрибут
  • ldap.MOD_REPLACE — заменить атрибут
  • ldap.MOD_DELETE — удалить атрибут (или одно из его значений)

Изменяю запись и смотрю результат:

>>> ad.modify_s(dn, modlist) (103, []) >>> show_entry(dn) DN = CN=Обломов Илья,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru cn = Обломов Илья sn = Обломоff givenName = Илья telephoneNumber = 

Метод rename_s объекта LDAPObject позволяет изменить RDN записи:

>>> ad.rename_s(dn, "cn=Обломов Илья Ильич") (109, [], 10, []) >>> show_entry(dn) Не найден DN CN=Обломов Илья Ильич,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru >>> dn = "CN=Обломов Илья Ильич,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru" >>> show_entry(dn) DN = CN=Обломов Илья Ильич,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru cn = Обломов Илья Ильич sn = Обломоff givenName = Илья telephoneNumber = 

Наконец, удаляю экспериментальную запись:

>>> ad.delete_s(dn) (107, [], 12, []) >>> show_entry(dn) Не найден DN CN=Обломов Илья Ильич,OU=Производственный отдел,O=Синее Небо,DC=org,DC=ru 

Закрываю соединение с Active Directory:

Все примеры выше используют синхронные операции с LDAP. Объект LDAPObject предоставляет также асинхронные методы search , add , modify , rename , delete . Эти методы возвращают целочисленный идентификатор операции, который затем нужно передать методу result для получения результата операции, или методу abandon — для отмены операции.

Были рассмотрены базовые возможности, предоставлемые пакетом python-ldap , для чтения и изменения данных в LDAP каталоге.

Источник

Оцените статью