Что такое код ldap_next_attribute

Содержание

Атрибут не переименовывается этим кодом в LDAP

Мой список ошибок выглядит следующим образом

Я не буду теперь делать то, что должен сделать код, но исключение говорит вам, что вы вставляете только

Код ошибки 32 — это объект, который не найден, или ошибка неправильного пути DN. Вы должны дать ему реальный и действительный DN для использования.

Вы отправили сообщение на печать так, чтобы строка ошибки: оставшееся имя ‘cn = name1 name2, ou = mycompany’ изменило значения или является реальным кодом ошибки?

Это выглядит странно, поскольку в другом месте вы не ссылаетесь на этот путь в своем коде. Я отмечаю, что вы используете домен (возможно, AD) с dc = mydomain, dc = com.

Вы, вероятно, можете уйти с относительными путями, но я сомневаюсь, что во время переименования, где вы меняете RDN, важно точно знать, что вы меняете (и неявно) туда.

Варианты использования для функций PHP «first_» и «next_» в сравнении с «get_» для атрибутов, значений, записей и т. Д. LDAP

Библиотека PHP LDAP определяет несколько классов функций для прохождения результатов поиска, «первая/следующая», функции и «получить» функции:

  • ldap_first_entry
  • ldap_next_entry
  • ldap_get_entries

и так далее для атрибутов, значений и т.д.

Каковы некоторые варианты использования для каждого из них (итерация по каждому результату или получение всех результатов в многомерном массиве), и для чего проще код?

Создан 02 окт. 12 2012-10-02 05:46:56 Andrew

2 ответа

Как и в любой базе данных, если вы ожидаете получить много результатов, будет предпочтительным повторение одного результата за раз. Альтернатива, получающая сразу все результаты, удобна, но может вызвать интенсивное использование памяти, если возвращаются сотни результатов.

Создан 02 окт. 12 2012-10-02 06:14:57 jimp

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

Например, file_get_contents() vs fopen() и fread() . Или XMLReader vs SimpleXML .

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

Эти функции LDAP одинаковы.

Загрузка их всех в память сразу быстрее и имеет преимущества, если вы хотите сделать больше, чем просто простую итерацию через список.

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

PHP дает вам выбор, потому что оба варианта имеют свои достоинства. Используйте то, что подходит вашему приложению.

Надеюсь, что это поможет.

Создан 02 окт. 12 2012-10-02 06:18:06 Spudley

Аутентификация LDAP — настройки

Страница Аутентификация LDAP служит для настройки сервера LDAP с целью аутентификации пользователей устройств (многофункциональное периферийное устройство, цифровой копир или устройство цифровой отправки). Если аутентификация LDAP выбрана в качестве Способа регистрации для одного или нескольких Функций устройств на странице Диспетчер аутентификации, пользователь должен ввести действительные имя пользователя и пароль для получения доступа к этим функциям.

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

На странице Аутентификация LDAP можно задать параметры доступа к серверу LDAP и поиска информации пользователя. Обратите внимание, что данную страницу можно использовать только в том случае, если LDAP выбран в качестве Способа регистрации на странице Диспетчер аутентификации.

Подключение к серверу LDAP

Способ привязки сервера LDAP

Параметр Способ привязки сервера LDAP определяет, как устройство будет осуществлять подключение к серверу LDAP. Обратитесь к администратору сервера LDAP, чтобы определить наиболее оптимальный для вас способ.

  • Простой — Выбранный сервер LDAP не поддерживает шифрование. Обратите внимание, что пароль (если он существует) будет отправлен по сети в открытом виде.
  • Простой через SSL — Выбранный сервер LDAP поддерживает шифрование с помощью протокола SSL. Все данные, в том числе имя пользователя и пароль, будут шифроваться. Сервер LDAP должен быть настроен для поддержки SSL, включая настройку сертификата для его идентификации. Кроме того, сетевой интерфейс устройства должен быть настроен с использованием сертификата Центра сертификации (CA) для проверки сервера LDAP. Сертификат CA настраивается на вкладке Сеть Web-интерфейса. В некоторых конфигурациях серверов LDAP необходимо также создавать сертификат клиента, который настраивается на этой же вкладке Сеть.

Сервер LDAP

Параметр Сервер LDAP представляет собой имя хоста или IP-адрес сервера LDAP, которое будет использоваться для аутентификации пользователей устройства. В случае использования SSL введенное здесь имя или адрес должны соответствовать имени в сертификате, направляемом сервером.

В этом поле можно указать несколько серверов, разделяя их адреса знаком вертикальной полосы (‘|’, ASCII 0x7c). Эту функцию можно, например, использовать для указания основного и резервного серверов. Сетевой интерфейс поддерживает поддерживает только один сертификат CA, поэтому все серверы LDAP в этом списке должны использовать один сертификат CA.

Параметр Порт определяет номер порта TCP/IP, на котором сервер обрабатывает запросы LDAP. Обычно это порт 389 для Простой привязки или 636 для привязок Простой через SSL.

Имя пользователя и пароль для поиска

В аутентификации LDAP используется два способа аутентификации пользователя.

Первый способ называется Имя и пароль пользователя устройства ; он предполагает “конструирование” DN (отличительного имени) пользователя для аутентификации (“привязки”) в каталоге LDAP. В начало информации, вводимой пользователем на панели управления, добавляется Префикс DN , и данная строка добавляется к строке Привязка и начало поиска . Например префикс DN “CN” в сочетании с введенной пользователем строкой john.doe@nasa.gov и строкой привязки и начала поиска OU=Engineering,DC=NASA,DC=GOV определит DN пользователя: CN=john.doe@nasa.gov,OU=Engineering,DC=NASA,DC=GOV

Второй способ называется Использование имени пользователя и пароля администратора и позволяет искать DN пользователя вместо его «реконструкции». В данном способе для первоначальной привязки используются регистрационные данные администратора (DN и пароль), и начинается поиск DN пользователя, проходящего аутентификацию. При обнаружении DN этого пользователя устройство предпримет попытку выполнить аутентификацию с помощью полученного значения DN и пароля, введенного пользователем на панели управления устройства. Если аутентификация выполнена успешно, считывается адрес электронной почты пользователя и он получает доступ к функциям устройства.

Способ Имя и пароль пользователя устройства следует использовать, когда все пользователи находятся в одном контейнере каталога LDAP, а первым следует слагаемое DN, которое пользователь обычно использует для аутентификации. Обратите внимание, что допускается ввод нескольких строк привязки и начала поиска при условии разделения их знаком “|”, и устройство предпримет попытки поочередно аутентифицировать пользователя по каждому из значений привязки и начала поиска. Данный способ может быть применен, если пользователи находятся в нескольких контейнерах каталога LDAP.

Способ Использование имени пользователя и пароля администратора следует применять в том случае, если пользователи находятся в нескольких контейнерах, либо если первое слагаемое DN обычно неизвестно некоторым пользователям или не используется для аутентификации в других системах. При использовании этого способа пользователю может быть предложено ввести уникальный атрибут LDAP, такой как SAMAccountName или даже номер телефона пользователя — атрибут TelephoneNumber.

Имя и пароль пользователя устройства

В этом способе используется префикс привязки — строка, которую пользователь вводит на панели управления устройства, а также строка Привязка и начало поиска для воссоздания DN пользователя. Воссозданный DN используется для аутентификации пользователя.

Префикс привязки

Параметр Префикс привязки — это атрибут LDAP, используемый для построения DN пользователя для целей аутентификации. Этот префикс комбинируется с именем пользователя, введенным на панели управления, образуя относительное отличительное имя (RDN). Обычно используется префикс «CN» (для общего имени) или «UID» (для идентификатора пользователя).

Использование имени пользователя и пароля администратора

Это DN (отличительное имя) пользователя, имеющего права чтения каталога LDAP. Введенная здесь учетная запись может не иметь административного доступа к каталогу. Прав чтения достаточно.

Пароль пользователя, чей DN введен в поле «DN администратора».

Поиск по базе данных LDAP

Привязка и начало поиска

При выборе способа Имя и пароль пользователя устройства значение Привязка и начало поиска используется на обоих этапах аутентификации. На этапе проверки имени пользователя и пароля это значение комбинируется с RDN для воссоздания полного отличительного имени пользователя (DN). На этапе поиска информации пользователя это значение является DN записи LDAP, с которой начинается поиск.

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

Строка состоит из пар «атрибут=значение», разделенных запятыми. Например:

При выборе способа «Имя и пароль пользователя устройства» в это поле можно ввести несколько несколько строк привязки, разделенных знаком вертикальной черты (‘|’, ASCII 0x7c). Это можно использовать, например, для указания альтернативных доменов LDAP. Устройство предпримет попытку привязать сервер LDAP, используя поочередно все строки в заданном порядке. После успешного выполнения привязки тот же корневой объект используется для поиска информации пользователя устройства.

Атрибут LDAP, соответствующий имени пользователя

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

Получение

адреса электронной почты, используя атрибут

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

и имя, используя атрибут

Экранное имя пользователя получают из атрибута LDAP, указанного в поле имя, используя атрибут.

Тестирование

Функция Тестирование используется для проверки правильности параметров перед их применением. При нажатии этой кнопки вам будет предложено ввести имя пользователя и пароль, как если бы вы регистрировались на панели управления устройства. Если введенные регистрационные данные прошли аутентификацию и информация пользователя найдена в базе данных LDAP, появляется сообщение об успешном выполнении проверки. В противном случае появится сообщение об ошибке с указанием на отрицательный результат аутентификации.

ldap_next_attribute function

For a given entry, the ldap_next_attribute function returns the next attribute.

Syntax

Parameters

The session handle.

The entry whose attributes are to be stepped through, as returned by ldap_first_entry or ldap_next_entry.

The address of a pointer used internally to track the current position in the entry.

Return Value

If the function succeeds, it returns a pointer to a null-terminated string that contains the current attribute name. If there are no more attributes to step through, it returns NULL. The session error parameter in the LDAP data structure is set to 0 in either case.

If the function fails, it returns NULL and sets the session error parameter in the LDAP data structure to the LDAP error code.

Remarks

Use ldap_next_attribute in conjunction with ldap_first_attribute to step through the list of attribute types returned with an entry. You can then pass these attribute names in a call to ldap_get_values to retrieve their associated values.

A call to ldap_next_attribute returns, through the ptr parameter, a pointer to a BerElement structure. Pass this pointer to the next call to ldap_next_attribute to track the current position in the list of attributes. When you have finished stepping through a list of attributes, and ptr is non-NULL, free the pointer by calling ber_free (ptr, 0). Be aware that you must pass the second parameter as 0 (zero) in this call.

The ldap_next_attribute function returns a pointer to an internally allocated buffer that contains the current attribute name. Free this buffer, when no longer required, by calling ldap_memfree. Because this buffer is overwritten on the next call to either ldap_first_attribute or ldap_next_attribute, the user should make a copy of the attribute name if it must be preserved for processing.

How to retrieve all LDAP attributes definition on LDAP database?

I’m working with ldap and want to retrieve all Ldap Attribute fields that defined on Ldap server. I just need list of attribute field only not the value. The result should be a list like this:

Is there way to do this?

4 Answers 4

Instead of making the LDAP return all the attributes for you it would be advisable to set the array of attributes your are interested through SearchControl.

You can enumerate all attributes of specific object (i.e. user in your case) and add them into a list as

Depending on the LDAP Server Implementation, the LDAP schema, which contains all the Attributes (and ObjectClasses) defined can be obtained using several methods as described at: http://ldapwiki.com/wiki/LDAP%20Query%20For%20Schema

If you only wanted the Attributes. Try something like:

You can use the getSchema() and get the Schema of tree root of your LDAP

DirContext schema = yourLDAPctx.getSchema(«»);

then you can also choose which all attributes of a class you want from the Schema

DirContext personSchema = (DirContext)schema.lookup(«ClassDefinition/ «);

You can refer this link for it..It will tell you in more detail

Что такое код ldap_next_attribute

LDAP (Lightweight Directory Access Protocol) — Протокол Доступа к Директориям (каталогам), является протоколом, используемым для доступа к «Серверам Каталогов». Директория является специальной разновидностью базы данных, которая хранит информацию используя древовидную структуру.

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

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

Прямая косая черта отмечает каждый раздел в ссылке, а вся последовательность символов ссылки читается слева направо.

Эквивалентом полностью определенной ссылки в LDAP является «distinguished name» (различаемое имя), обозначаемое просто как «dn». Примером dn может быть:

cn=John Smith,ou=Accounts,o=My Company,c=US

Каждый раздел такой ссылки отмечается запятой, а вся последовательность читается справа налево. Ссылка читается как ..

country = US
organization = My Company
organizationalUnit = Accounts
commonName = John Smith

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

Поиск информации для всех записей, где фамилия начинается с «S», в сервере директории, вывод на дисплей и извлечение с именем и email-адресом.

Пример 1. Пример поиска в LDAP

Использование PHP LDAP вызовов

Вам потребуется установить и скомпилировать библиотеки LDAP-клиента или из пакета University of Michigan ldap-3.3, или из Netscape Directory SDK. Вам также потребуется перекомпилировать PHP с поддержкой LDAP для того чтобы применение PHP LDAP вызовов стало доступным.

Прежде чем использовать LDAP вызовы, необходимо знать ..

  • Имя или адрес сервера директории, который вы будете использовать
  • «Базовый dn» сервера (часть «мирового» каталога на данном сервере, которая может быть «o=My Company,c=US»)
  • Нужен ли пароль для доступа к данному серверу (многие серверы обеспечивают доступ для чтения для «anonymous связей» но требуют пароля для чего-либо еще)

Типичная последовательность LDAP-вызовов, которую вы можете применять в приложениях, представлена в следующем щаблоне:

ldap_connect() // установка соединения с сервером
|
ldap_bind() // анонимный или идентифицируемый «вход»
|
действия подобные поиску или обновлению каталога
с выводом результата

Дополнительная информация

Netscape SDK одержит полезное Руководство Программиста в .html формате.

ldap_add

ldap_add — добавляет записи в LDAP каталог

Описание

int ldap_add (целочисленный link_identifier, строковое dn, массив записи);

возвращает true при успехе и false при ошибке.

Функция ldap_add() используется для добавления записей в LDAP каталог. DN добавляемой записи выражается посредством dn. Массив записи определяет информацию о записи. Значения записей индексируются посредством индивидуальных атрибутов. В случае множественных значений для атрибута, они индексируются целыми числами начиная с 0.

запись[«атрибут1»] = значение
запись[«атрибут2»][0] = значение1
запись[«атрибут2»][1] = значение2

Пример 1. Полный прример с идентифицируемой связью

ldap_bind

ldap_bind — связь с LDAP каталогом

Описание

int ldap_bind (целое link_identifier, строковое bind_rdn, строковое bind_password);

Связь с LDAP каталогом с определенным RDN и паролем. Возвращает true при успехе и false при ошибке.

ldap_bind() осуществляет операцию связи с каталогом. bind_rdn и bind_password используются факультативно. Если не определено, применяется связь anonymous.

ldap_close

ldap_close — закрывает связь с LDAP сервером

Описание

int ldap_close (целое link_identifier);

Возвращает true при успехе, false при ошибке.

ldap_close() закрывает связь с LDAP сервером, которая ассоциировалась с определенным link_identifier.

Этот вызов внутренне идентичен ldap_unbind(). LDAP API использует вызов ldap_unbind(), поэтому возможно он предпочтительнее вызова ldap_close().

ldap_connect

ldap_connect — соединение с LDAP сервером

Описание

int ldap_connect (строковое hostname, целое port);

Возвращает положительный LDAP идентификатор связи при успехе, false при ошибке.

ldap_connect() устанавливает соединение с LDAP сервером по определенным hostname и port. Оба аргумента факультативные. Если аргументы не определены, то будет возвращен идентефикатор уже открытого соединения. Если определено только hostname, то по умолчанию используется порт 389.

ldap_count_entries

ldap_count_entries — подсчет количества записей при поиске

Описание

int ldap_count_entries (целое link_identifier, целое result_identifier);

Возвращает количество записей в результате или false при ошибке.

ldap_count_entries() возвращает количество записей хранимых в результате от предыдущей операции поиска. result_identifier идентифицирует внутренний ldap результат.

ldap_delete

ldap_delete — удаляет запись из каталога

Описание

int ldap_delete (целое link_identifier, строковое dn);

Возвращает true при успехе и false при ошибке.

ldap_delete() удаляет отдельную запись из LDAP каталога, определенную по dn.

ldap_dn2ufn

ldap_dn2ufn — конвертирует DN в User Friendly Naming формат

Описание

string ldap_dn2ufn (строковое dn);

ldap_dn2ufn() преобразует DN в более дружественную для пользователя форму, удаляя имена типа.

ldap_explode_dn

ldap_explode_dn — разбивает DN на составные части

Описание

array ldap_explode_dn (строковое dn, целое with_attrib);

ldap_explode_dn() разбивает DN возвращаемое по ldap_get_dn() на составные части. Каждая часть известна как Relative Distinguished Name, или RDN. ldap_explode_dn() возвращает массив всех компонентов. with_attrib используется для запроса, возвращать ли RDN толъко со значениями или также с их атрибутами. Чтобы получить RDN-части с атрибутами (т.е. в формате атрибут=значение) установите with_attrib в 1, чтобы получить только значения установите его в 0.

ldap_first_attribute

ldap_first_attribute — возвращает первый атрибут

Описание

string ldap_first_attribute (целое link_identifier, целое result_entry_identifier, целое ber_identifier);

Возвращает первый атрибут в записи при успехе и false при ошибке.

Подобно чтению записей, атрибуты также читаются один за другим из отдельной записи. ldap_first_attribute() возвращает первый атрибут в записи, отмеченной идентификатором записи. Оставшиеся атрибуты ищутся последовательными вызовами ldap_next_attribute(). ber_ >ber_identifier передается ldap_next_attribute() функции, которая изменяет этот указатель.

ldap_first_entry

ldap_first_entry — возвращает первый идентификатор (id) результата

Описание

int ldap_first_entry (целое link_identifier, целое result_identifier);

Возвращает идентификатор записи для первой записи результата при успехе и false при ошибке.

Записи в LDAP-результате считываются последовательно с использованием функций ldap_first_entry() и ldap_next_entry(). ldap_first_entry() возвращает идентификатор записи для первой записи в результате. Этот идентификатор записи передается затем в процедуру lap_next_entry() для получения последовательных записей из результата.

ldap_free_result

ldap_free_result — освобождает память результата

Описание

int ldap_free_result (целое result_identifier);

Возвращает true при успехе и false при ошибке.

ldap_free_result() освобождает внутреннюю память, предназначенную для хранения результата и отмечаемую посредством result_identifier. Вся память результата автоматически освобождается когда скрипт завершается.

Обычно вся память, выделяемая для ldap результата освобождается при окончании скрипта. В случае, когда скрипт выполняет последовательные поиски, которые возвращают большие наборы записей в результате, ldap_free_result() может быть вызвана для сохранения работоспособности оперативной памяти для следующей части скрипта..

ldap_get_attributes

ldap_get_attributes — получает атрибуты записи в результате от поиска

Описание

array ldap_get_attributes (целое link_identifier, целое result_entry_identifier);

Возвращает полную информацию о записи в многоразмерном массиве при успехе и false при ошибке.

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

Разместив определенную запись в каталоге, вы можете узнать какая информация хранится для этой записи, используя данный вызов. Вы могли бы использовать этот вызов в приложении которое «просматривает» каталог записей и/или когда вам не известна структура каталога записей. Во многих приложениях вы можете искать определенные атрибуты, такие как email-адрес или фамилия, не озадачиваясь при этом содержимым других данных.

return_value[«count»] = количество атрибутов в записи
return_value[0] = первый атрибут
return_value[n] = n-ый атрибут

return_value[«attribute»][«count»] = количество значений атрибута
return_value[«attribute»][0] = первое значение атрибута
return_value[«attribute»][i] = i-тое значение атрибута

Пример 1. Показывает список атрибутов отдельной записи каталога

ldap_get_dn

ldap_get_dn — получает DN записи результата

Описание

string ldap_get_dn (целое link_identifier, целое result_entry_identifier);

Возвращает DN записи результата или false при ошибке.

ldap_get_dn() используется для нахождения DN записи в результате.

ldap_get_entries

ldap_get_entries — получает все записи результата

Описание

array ldap_get_entries (целое link_identifier, целое result_identifier);

Возвращает полную информацию о результате в многомерном массиве при успехе и false при ошибке.

ldap_get_entries() используется для упрощения чтения множества записей из результата и затем чтения атрибутов и множественных значений. Информация о записи возвращается по одиночному вызову функции в многомерном массиве. Структура массива представлена ниже.

Индекс атрибута преобразуется к нижнему регистру. (Атрибуты серверов каталогов нечувствительны к регистру, но не в том случае когда они используются в качестве индексов массива).

return_value[«count»] = количество записей в результате
return_value[0] : ссылается на детали первой записи

return_value[i][«dn»] = DN i-той записи в результате

return_value[i][«count»] = количество атрибутов i-той записи
return_value[i][j] = j-тый атрибут i-той записи результата

return_value[i][«attribute»][«count»] = количество значений атрибута в i-той записи
return_value[i][«attribute»][j] = j-тое значение атрибута в i-той записи

ldap_get_values

ldap_get_values — получение всех значений из записи результата

Описание

array ldap_get_values (целое link_identifier, целое result_entry_identifier, строковое attribute);

Возвращает массив значений атрибута при успехе и false при ошибке.

ldap_get_values() используется для чтения всех значений атрибута в записи в данном результате. Запись определяется по result_entry_identifier. Количество значений может быть получено при индексации «счетчика» в результирующем массиве. Отдельные значения доступны по целочисленному индексу в массиве. Первый индекс начинается с 0.

Для данного вызова необходим result_entry_identifier, поэтому нужно предварительно сделать один вызов ldap поиска, и один из вызовов для получения отдельной записи.

Ваше приложение или может быть жестко настроено на поиск определенных атрибутов (таких как «фамилия» или «почта») или вы должны использовать вызов ldap_get_attributes() для получения информации о том, какие атрибуты существуют для данной записи.

В LDAP может быть более одной записи для атрибута, поэтому можно, например, хранить несколько адресов email в записи каталога для одной персоны, при этом все записи будут отмечены с атрибутом «mail»

return_value[«count»] = количество значений для атрибута
return_value[0] = первое значение атрибута
return_value[i] = i-тое значение атрибута

Пример 1. Список значений атрибута «mail» для записи каталога

ldap_list

ldap_list — одноуровневый поиск

Описание

int ldap_list (целое link_identifier, строковое base_dn, строковое filter);

Возвращает идентификатор результата поиска или false при ошибке.

ldap_list() выполняет поиск с определенным фильтром по каталогу с областью LDAP_SCOPE_ONELEVEL.

LDAP_SCOPE_ONELEVEL означает что такой поиск может вернуть только информацию, находящуюся на уровне непосредственно ниже базового dn, заданного в вызове. (Эквивалентно вводу «ls» и получению списка файлов и папок в текущем рабочем каталоге).

Этот вызов берет факультативно четвертый параметр который является массивом требуемых атрибутов. См. примечание к ldap_search().

Пример 1. Составление списка всех подразделений организации

ldap_modify

ldap_modify — изменение записи LDAP

Описание

int ldap_modify (целое link_identifier, строковое dn, массив entry);

Возвращает true при успехе и false при ошибке.

ldap_modify() используется для изменения существующих записей в каталоге LDAP. Структура записи такая же как и в ldap_add().

ldap_next_attribute

ldap_next_attribute — получает следующий атрибут в результате

Описание

string ldap_next_attribute (целое link_identifier, целое result_entry_identifier, целое ber_identifier);

Возвращает следующий атрибут в записи или false при ошибке.

ldap_next_entry

ldap_next_entry — получает следующую запись в результате

Описание

int ldap_next_entry (целое link_identifier, целое result_entry_identifier);

Возвращает идентефикатор записи для следующей записи в результате, записи которого начинали считываться функцией ldap_first_entry(). Если больше нет записей в результате, то возвращается false.

ldap_read

ldap_read — чтение записи

Описание

int ldap_read (целое link_ >attributes ]);

Возвращает идентификатор результата поиска или false при ошибке.

ldap_read() выполняет поиск при определенном фильтре по каталогу с областью LDAP_SCOPE_BASE. Таким образом, это эквивалентно чтению записи из каталога.

Пустой фильтр не допустим. Если вы хотите получить абсолютно всю информацию для данной записи, используйте фильтр «object .

Этот вызов берет факультативно четвертый параметр который является массивом требуемых атрибутов. См. примечание ldap_search().

ldap_search — поиск по дереву LDAP

Описание

int ldap_search (целое link_ >attributes ]);

Возвращает идентификатор результата поиска или false при ошибке.

ldap_search() осуществляет поиск для определенного фильтра по каталогу с областью LDAP_SCOPE_SUBTREE. Это эквивалентно поиску по всему каталогу. base_dn определяет базовый DN для данного каталога.

Имеется факультативный четвертый параметр, который может быть добавлен для ограничения атрибутов и значений возвращаемых сервером, если это требуется. Это значительно эффективнее чем действие по умолчанию (которое возвращает все атрибуты и их соответствующие значения). Использование четвертого параметра может поэтому рассматриваться как хорошая практика.

Четвертый параметр является стандартным строковым массивом PHP с требуемыми атрибутами, т.е. array(«mail»,»sn»,»cn»). Заметим, что «dn» требуется всегда, независимо от того, какие типы атрибутов запрашиваются.

Отметим также, что некоторые хосты каталога сервера могут быть сконфигурированы так, что будут возвращать количество записей не превышающее предварительно установленное количество. Если это происходит, сервер будет показывать что он будет возвращать только ограниченные наборы результатов.

Поисковый фильтр может быть простым или расширенным, использующим булевы операторы в формате описанном в документации LDAP (См. Netscape Directory SDK для дополнения информации по фильтрам).

Приведенный ниже пример отыскивает the отдел организации, фамилию, данное имя и адрес email для всех людей в «My Company» где фамилия или данное имя содержат подстроку $person. Этот пример использует логический фильтр для указания серверу на поиск информации более чем в одном атрибуте.

Пример 1. LDAP поиск

Когда вы выполняете поиск, и слишком много данных возвращается (много записей) вы получите предупреждение, и ldap_get_entries() выдаст сбой. Рассматриваемый здесь прием должен выключить это предупреждение, потом проверить полученную запись.

$normerr = error_reporting ();
error_reporting (0); // выключает предупреждение!
$sr = ldap_search ($ds, $dn, $searchfor);
$normerr = error_reporting ($normerr);
if (!$sr) <
print «слишком много записей!»;
> else .

Вы можете попробовать сузить эту область, добавив особый фильтр, т.е. (cn=a*), но было бы лучше иметь возможность захватить результаты в битах (т.е. 1-100, 101-200. ).

ldap_unbind

ldap_unbind — прекращение связи из каталога LDAP

Описание

int ldap_unbind (целое link_identifier);

Возвращает true при успехе и false при ошибке.

ldap_unbind() прекращает связь из каталога LDAP.

Основы протокола LDAP: иерархия данных и компоненты записей

LDAP (или Lightweight Directory Access Protocol) – открытый протокол, используемый для хранения и извлечения данных из иерархической структуры каталогов. Обычно используется для хранения информации об организации и ее активах и пользователях. LDAP – это гибкое решение для определения любого объекта и его качеств.

Для многих пользователей LDAP – очень сложный инструмент, поскольку он использует специальную терминологию, некоторые необычные сокращения и часто реализуется как компонент более крупной системы взаимодействующих частей. В этом мануале вы ознакомитесь с основами LDAP.

Что такое служба каталогов?

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

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

Что такое LDAP?

LDAP (или lightweight directory access protocol) – это протокол, который определяет методы, с помощью которых можно получить доступ к службе каталогов. В более широком смысле LDAP формирует способ отображения данных в службе каталогов для пользователей, определяет требования к компонентам, используемым для создания записей данных, и описывает способ использования разных примитивных элементов для составления записей.

Поскольку LDAP является открытым протоколом, существует множество различных его реализаций. Проект OpenLDAP является одним из наиболее хорошо поддерживаемых вариантов LDAP с открытым исходным кодом.

Основные компоненты данных LDAP

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

Атрибуты

Сами данные в системе LDAP в основном хранятся в элементах, называемых атрибутами. Атрибуты – это в основном пары ключ-значение. В отличие от некоторых других систем, ключи – это предопределенные имена, которые продиктованы объектными классами записи (мы обсудим это немного позже). Кроме того, данные в атрибуте должны соответствовать типу, указанному в первоначальном определении атрибута.

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

Когда речь идет об атрибуте и его данных, обе части соединяются знаком равенства:

Значения атрибутов содержат большинство фактических данных, которые нужно сохранить в системе LDAP. Другие элементы в LDAP используются для определения структуры, организации и т. д.

Записи

Атрибуты сами по себе бессмысленны. Чтобы иметь смысл, они должны быть связаны с чем-то. В LDAP атрибуты находятся внутри записи. Запись представляет собой набор атрибутов под описательным именем.

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

Запись, отображаемая в LDIF (LDAP Data Interchange Format), будет выглядеть примерно так:

dn: sn=Amber,ou=people,dc=8host,dc=com
objectclass: person
sn: Amber
cn: Justin Amber

Вышеприведенный пример может быть допустимой записью в системе LDAP.

Возможно, вы уже заметили, что данные, определенные атрибутами, представляют собой часть доступной информации об объекте. Остальное – это размещение записи в системе LDAP и отношения, которые она подразумевает.

Например, если возможно иметь записи для пользователя и для элемента инвентаря, как их отличить? Один из способов различать записи разных типов – это установить отношения и группы. Это по сути функция, где помещается запись при ее создании. Все записи добавляются в систему LDAP в виде веток в деревьях, называемых информационными деревьями каталога, или DIT.

DIT представляет собой организационную структуру, похожую на файловую систему, где каждая запись (отличная от записи верхнего уровня) имеет ровно одну родительскую запись и может содержать любое количество дочерних записей под ней. Поскольку записи в дереве LDAP могут представлять собой что угодно, некоторые записи будут использоваться в основном для организационных целей, аналогично каталогам в файловой системе.

Таким образом, у вас может быть запись people и отдельная запись inventoryItems. А фактические записи данных могут быть дочерними по отношению к ним, что позволит различать их тип. Ваши организационные записи можно произвольно определить для наилучшего представления ваших данных.

В примере, приведенном в предыдущем разделе, мы видим один признак DIT в строке dn:

Эта строка называется различительным именем записи (подробнее об этом позже) и используется для идентификации записи. Она функционирует как полный путь назад к root DIT. В этом случае есть запись sn=Amber. Прямым родителем является запись по имени ou=people, которая, вероятно, используется в качестве контейнера для записей, описывающих людей. Родители этой записи происходят из домена 8host.com, который функционирует как root DIT.

Компоненты данных LDAP

В последнем разделе мы обсудили, как данные представлены в системе LDAP. Однако также нужно поговорить о том, как определяются компоненты, хранящие данные. Например, ранее мы упоминали, что данные должны соответствовать типу, определенному для каждого атрибута. Откуда берутся эти определения? Давайте начнем с самых простых компонентов.

Определения атрибутов

Атрибуты определяются с помощью довольно запутанного синтаксиса. Они должны содержать имя атрибута, любые другие имена, которые могут использоваться для ссылки на атрибут, тип данных, а также множество других метаданных. Эти метаданные могут описывать атрибут, помогать сортировать или сравнивать значения атрибута и описывать, как он относится к другим атрибутам.

Например, это определение атрибута name:

attributetype ( 2.5.4.41 NAME ‘name’ DESC ‘RFC4519: common supertype of name attributes’
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 <32768>)

‘name’ – это имя атрибута. Число в первой строке – это глобальный уникальный идентификатор OID (Object ID), присвоенный атрибуту, чтобы отличать его от остальных атрибутов. Остальная часть записи определяет, как ее можно сравнивать во время поиска, и указывает, где найти информацию о типе данных атрибута.

Важной частью определения атрибута является то, может ли атрибут быть определен в записи более чем один раз. Например, фамилия может быть определена только один раз для каждой записи, но атрибут родства (например niece) может присутствовать в одной записи несколько раз. Атрибуты по умолчанию многозначны; если атрибут можно установить только один раз для каждой записи, он должен содержать флаг SINGLE-VALUE.

Определения атрибутов намного сложнее, чем использование и установка атрибутов. К счастью, по большей части вам не придется определять свои собственные атрибуты, потому что наиболее распространенные из них включены в большинство реализаций LDAP, а другие можно легко импортировать.

Определения ObjectClass

Атрибуты собираются внутри объектов, называемых objectClasses. ObjectClasses – это просто группы связанных атрибутов, которые были бы полезны при описании конкретной вещи. Например, «person» является objectClass.

Записи получают возможность использовать атрибуты objectClass, устанавливая специальный атрибут objectClass, который нужно использовать. Фактически, objectClass – единственный атрибут, который вы можете установить в записи без указания дополнительного objectClass.

Поэтому, если вы создаете запись для описания человека, в том числе objectClass person, вы можете использовать в нем все атрибуты:

dn: . . .
objectClass: person

Тогда у вас будет возможность установить эти атрибуты внутри записи:

  • cn: имя
  • description: удобочитаемое описание записи
  • seeAlso: ссылка на соответствующие записи
  • sn: фамилия
  • telephoneNumber: номер телефона
  • userPassword: пароль для пользователя

Атрибут objectClass можно использовать несколько раз, если вам нужны атрибуты из разных objectClasses, но есть правила, их использования.

Два основных типа ObjectClasses – это структурный и вспомогательный. Запись должна иметь ровно один структурный класс, но может иметь ноль или больше вспомогательных классов, используемых для расширения атрибутов класса. Структурный objectClass используется для создания и определения записи, в то время как вспомогательные objectClasses вносят дополнительную функциональность через новые атрибуты.

Определения ObjectClass указывают, требуются ли предлагаемые атрибуты (указывается спецификацией MUST) или их использовать необязательно (обозначается спецификацией MAY). Несколько objectClasses могут предоставлять одинаковые атрибуты, а атрибут MAY или MUST может отличаться.

Например, objectClass person определяется так:

objectclass ( 2.5.6.6 NAME ‘person’ DESC ‘RFC2256: a person’ SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )

Он определяется как структурный objectClass, что означает, что его можно использовать для создания записи. Созданная запись должна указывать атрибуты surname и commonname и может указывать атрибуты userPassword, telephoneNumber, seeAlso или description.

Схемы

Определения ObjectClass и определения атрибутов, в свою очередь, группируются в схемы. В отличие от традиционных реляционных баз данных, схемы в LDAP – это просто коллекции связанных объектов и атрибутов. Один DIT может иметь множество разных схем, чтобы создавать записи и атрибуты, которые ему нужны.

Схемы часто включают дополнительные определения атрибутов и могут потребовать атрибуты, определенные в других схемах. Например, objectClass person, о котором мы говорили ранее, требует, чтобы атрибут surname или sn были установлены во всех записях, которые используют objectClass person. Если они не определены на LDAP-сервере, схема, содержащая эти определения, может использоваться для добавления этих определений в словарь.

Формат схемы в основном представляет собой комбинацию указанных выше записей, например:

. . .
objectclass ( 2.5.6.6 NAME ‘person’ DESC ‘RFC2256: a person’ SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
attributetype ( 2.5.4.4 NAME ( ‘sn’ ‘surname’ )
DESC ‘RFC2256: last (family) name(s) for which the entity is known by’ SUP name )
attributetype ( 2.5.4.4 NAME ( ‘cn’ ‘commonName’ )
DESC ‘RFC4519: common name(s) for which the entity is known by’ SUP name )
. . .

Организация данных

Мы рассмотрели общие элементы, которые используются для создания записей в системе LDAP, и узнали о том, как эти блоки определяются в системе. Теперь нужно посмотреть, как в LDAP DIT организована и структурирована информация.

Размещение записей в DIT

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

Вершина DIT – это самая широкая категория, которая так или иначе является родительской для всех узлов. Как правило, самая верхняя запись просто используется как метка, указывающая организацию, для которой используется DIT. Эти записи могут быть представлены любыми объектными классами, но обычно они создаются с помощью компонентов домена (dc=example,dc=com для example.com), расположения (l=new_york,c=us) или организационных сегментов (ou=marketing,o=Example_Co).

Записи организации (папки) часто используют организационный объект object >

Именование и ссылки на записи в DIT

Мы ссылаемся на записи по их атрибутам. Это означает, что каждая запись должна иметь атрибут или группу атрибутов, которые однозначны на своем уровне в иерархии DIT. Этот атрибут или группа атрибутов называется относительным отличительным именем записи (или RDN) и функционирует как имя файла.

Чтобы однозначно сослаться на запись, используйте RDN записи в сочетании со всеми RDN-элементами своих родительских записей. Эта цепочка RDN возвращает к вершине иерархии DIT и обеспечивает однозначный путь к рассматриваемой записи. Эта цепочка называется отличительным именем записи (или DN). DN для записи указывается во время создания, чтобы система LDAP знала, где разместить новую запись (тогда RDN записи не будет использоваться другой записью).

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

Например, запись для человека по имени John Smith может быть помещена под записью «People» для организации example.com. Поскольку в организации может быть несколько человек по имени John Smith, ID пользователя может быть лучшим выбором для RDN записи. Запись может быть указана следующим образом:

dn: u > objectClass: inetOrgPerson
cn: John Smith
sn: Smith
uid: jsmith1

Здесь нужно использовать objectClass inetOrgPerson, чтобы получить доступ к атрибуту uid в этом экземпляре (у вас все еще есть доступ ко всем атрибутам, определенным в объекте personClass).

Наследование LDAP

В основном отношения данных в LDAP-системе определяются иерархиями, наследованием и вложенностью. LDAP изначально кажется сложным средством, потому что он реализует некоторые объектно-ориентированные концепции в своей структуре. Это главным образом связано с использованием классов и наследования, о чем мы поговорим сейчас.

Наследование objectClass

Каждый objectClass – это класс, который описывает характеристики объектов этого типа.

Однако, в отличие от простого наследования, объекты в LDAP могут быть и часто являются экземплярами нескольких классов (некоторые языки программирования предоставляют аналогичную функциональность посредством множественного наследования). Это возможно, потому что концепция LDAP класса – это просто набор атрибутов, которые он должен или может иметь (MUST или MAY). Это позволяет указать для записи несколько классов (хотя может и должен присутствовать только один objectClass STRUCTURAL), в результате чего объект просто имеет доступ к объединенной коллекции атрибутов с наивысшим объявлением MUST или MAY.

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

objectclass ( 2.5.6.7 NAME ‘organizationalPerson’ SUP person STRUCTURAL
. . .

Родительский objectClass следует за идентификатором SUP. Родитель должен использовать тип objectClass определяемого objectClass (например, STRUCTURAL или AUXILIARY). Дочерний objectClass автоматически наследует атрибуты и требования родительского.

При назначении objectClass нужно указать только конкретного потомка цепочки наследования, чтобы получить доступ ко всем атрибутам. В последнем разделе мы использовали это, чтобы указать inetOrgPerson как единственный objectClass для записи John Smith, все еще имея доступ к атрибутам, определенным в классах person и organizationPerson. Иерархия наследования inetOrgPerson выглядит следующим образом:

inetOrgPerson -> organizationalPerson -> person -> top

Почти все деревья наследования objectClass заканчиваются специальным objectClass под названием «top». Это абстрактный objectClass, единственная цель которого – потребовать, чтобы объект objectClass был установлен. Он используется для обозначения верхушки цепочки наследования.

Наследование атрибутов

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

Это часто используется для создания более конкретных версий общего атрибута. Например, surname является типом имени и может использовать все те же методы для сравнения и проверки равенства. Атрибут может наследовать эти качества, чтобы получить общую форму атрибута name. Фактически определение surname может содержать немного больше, чем указатель на родительский атрибут.

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

Варианты протокола LDAP

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

Стоит отметить некоторые варианты LDAP в обычном формате:

  • ldap://: Это базовый протокол LDAP, который позволяет осуществлять структурированный доступ к службе каталогов.
  • ldaps://: Этот вариант используется для поддержки LDAP по протоколу SSL/TLS. Обычный LDAP-трафик не зашифрован, хотя большинство реализаций LDAP поддерживают его. Этот метод шифрования соединений LDAP фактически устарел, и вместо этого рекомендуется использовать шифрование STARTTLS. Если вы работаете с LDAP по небезопасной сети, настоятельно рекомендуется перестать это делать и настроить шифрование.
  • ldapi://: Поддержка LDAP на IPC. Это часто используется для безопасного соединения с локальной системой LDAP в административных целях. Он связывается с внутренними сокетами вместо открытого сетевого порта.

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

Заключение

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

Варианты использования для функций PHP «first_» и «next_» в сравнении с «get_» для атрибутов, значений, записей и т. Д. LDAP

Библиотека PHP LDAP определяет несколько классов функций для прохождения результатов поиска, «первая/следующая», функции и «получить» функции:

  • ldap_first_entry
  • ldap_next_entry
  • ldap_get_entries

и так далее для атрибутов, значений и т.д.

Каковы некоторые варианты использования для каждого из них (итерация по каждому результату или получение всех результатов в многомерном массиве), и для чего проще код?

Создан 02 окт. 12 2012-10-02 05:46:56 Andrew

2 ответа

Как и в любой базе данных, если вы ожидаете получить много результатов, будет предпочтительным повторение одного результата за раз. Альтернатива, получающая сразу все результаты, удобна, но может вызвать интенсивное использование памяти, если возвращаются сотни результатов.

Создан 02 окт. 12 2012-10-02 06:14:57 jimp

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

Например, file_get_contents() vs fopen() и fread() . Или XMLReader vs SimpleXML .

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

Эти функции LDAP одинаковы.

Загрузка их всех в память сразу быстрее и имеет преимущества, если вы хотите сделать больше, чем просто простую итерацию через список.

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

PHP дает вам выбор, потому что оба варианта имеют свои достоинства. Используйте то, что подходит вашему приложению.

Надеюсь, что это поможет.

Создан 02 окт. 12 2012-10-02 06:18:06 Spudley

Использование протокола LDAP в скриптах

Введение

Протокол LDAP (Lightweight Directory Access Protocol, упрощённый протокол для доступа к каталогу) служит для доступа к службам каталогов, в том числе Active Directory в Windows 2000/2003. Провайдер LDAP является одним из провайдеров ADSI (Active Directory Service Interface). Все примеры сценариев в настоящей статье будут приводиться на языке Python.

Служба каталогов Active Directory является LDAP-совместимой реализацией службы каталогов Microsoft для операционных систем семейства Windows и хранит информацию о сетевых ресурсах — пользователях, компьютерах, файлах, папках и принтерах, а также информацию безопасности, касающуюся этих ресурсов. Active Directory упрощает администрирование путём централизации управления. Домен — это логическая группа компьютеров и метод организации административной безопасности. Логическая структура Active Directory состоит из доменов, лесов и деревьев, а физическая структура состоит из серверов — контроллеров домена и сайтов (IP-подсетей). Active Directory использует модель репликации, когда копия каталога существует на каждом контроллере домена, что повышает отказоустойчивость (домен может содержать один или более контроллеров домена). Сайт (Site) Active Directory — это совокупность одной или нескольких IP-подсетей, объединенных высокоскоростными каналами связи. Сайт может содержать один или более доменов, а домен может быть размещен в нескольких сайтах. Сайты предназначены для управления репликацией Active Directory между сетями, соединёнными каналами связи с низкой пропускной способностью.

Домены в Active Directory организованы в иерархические структуры — деревья. Ветви такого дерева называются поддоменами. Имя дочернего домена включает имя родительского, например: microsoft.com, msdn.microsoft.com, vb.msdn.microsoft.com и т.д. Лес представляет собой группу деревьев, связанных доверительными отношениями — контроллеры домена в одном домене доверяют пользователям из другого домена (дают возможность использовать свои ресурсы). Отношения доверия транзитивны, то есть если домен А доверяет домену Б, а домен Б доверяет домену В, то домен А также доверяет домену В. Все деревья в лесу используют общую схему и общий глобальный каталог (Global Catalog, GC). Доверительными отношениями связываются корневые домены деревьев леса.

Схема (Schema) содержит формальное описание содержания и структуры хранилища Active Directory, включая все атрибуты, классы и свойства классов. Для каждого класса объектов схема определяет, какими атрибутами должен обладать экземпляр класса, какие дополнительные атрибуты он может иметь и какой класс объектов является предком текущего класса. При установке Active Directory создаётся стандартная схема, содержащая определения наиболее часто используемых объектов и их свойств, таких как пользователи, группы, компьютеры, принтеры и т.п. Схема Active Directory расширяема, т.е. вы можете определить новые типы объектов каталога и их атрибуты, в том числе и новые атрибуты для существующих объектов. Схема хранится вместе с данными Active Directory в глобальном каталоге и обновляется динамически (т.е. добавленные и изменённые атрибуты можно использовать практически сразу). Схема определяет структуру леса Active Directory, который может содержать несколько деревьев, которые, в свою очередь, могут содержать несколько иерархически структурированных доменов. В лесе Active Directory один из контроллеров объявляется мастером схемы (Schema Master) и отвечает за репликацию данных схемы на все контроллеры доменов. Cтруктурой леса и деревьев Active Directory является конфигурация (Configuration).

Глобальный каталог (Global Catalog, GC) — это центральное хранилище информации об объектах в дереве доменов и лесе Active Directory. Глобальный каталог является физическим хранилищем части атрибутов всех объектов леса Active Directory. Процесс частичной репликации позволяет находить большинство сведений непосредственно в глобальном каталоге, без обращения к исходному домену. По умолчанию в глобальном каталоге хранятся атрибуты, наиболее часто используемые при поиске (например имя пользователя, его учетной записи и т.п.), а также сведения, необходимые для обнаружения объекта (полный LDAP-путь к соответствующему объекту каталога). Хранение только основных атрибутов объектов в глобальном каталоге позволяет уменьшить его размер, поэтому один сервер глобального каталога может обслуживать много контроллеров домена Windows. При установке первого контроллера домена в лесу Active Directory он становится сервером глобального каталога. При наличии нескольких контроллеров домена можно перенести глобальный каталог на другой сервер или настроить несколько серверов на его поддержку.

В ADSI предусмотрен специальный провайдер «ADs», который возвращает ссылку на объект IADsNamespaces, который можно использовать для получения информации об установленных на компьютере провайдерах ADSI:

Корневой объект пространства имён провайдера LDAP (объект IADsNamespace) может быть получен следующим образом:

LDAP имеет иерархическую модель данных. Вы можете исследовать структуру ADSI с помощью оснастки ADSI Edit для MMC из пакета Windows Support Tools (пакет доступен для скачивания на сайте Microsoft). Для идентификации объектов существует два вида имен:

  • Отличительное (характерное) имя — Distinguished Name (DN).
  • Относительное отличительное (характерное) имя — Relatively Distinguished Name (RDN).

Отличительное имя объекта содержит полный путь к этому объекту в иерархии. Относительное отличительное имя объекта — часть отличительного имени, являющаяся именем объекта.

Кроме того, в каталоге существуют организационные блоки (организационные единицы или подразделения) Organization Units — OU, с помощью которых администратор каталога может создавать иерархию логических групп записей. Организационное подразделение — это контейнерный объект, предназначенный для группировки других объектов в логические административные группы в рамках домена. Организационные подразделения могут содержать такие объекты, как учетные записи пользователей, группы, компьютеры, принтеры и т.п. Иерархия организационных единиц домена не зависит от других доменов — каждый домен может поддерживать свою собственную иерархию. Часто групповые политики применяются именно к подразделениям (групповые политики сами являются объектами Active Directory).

Пользователь или группа имеет «дружественное» имя – основное имя пользователя (UPN – User Principal Name). Основное имя пользователя составляется из «сокращённого» имени пользователя и имени домена DNS, где находится объект, определяющий пользователя. UPN известны как адреса электронной почты. Например, пользователь James Smith в дереве microsoft.com может иметь UPN вида «JamesS@Microsoft.com».

Каждый объект каталога обладает глобальным уникальным идентификатором (Globally Unique Identifier, GUID) — 128-битным числом, генерируемым при создании объекта. GUID не изменяется в течение всего срока жизни объекта, даже при его переименовании или перемещении в другой домен. Поэтому приложения могут хранить GUID объекта и всегда однозначно находить его в каталоге.

Аббревиатуры, используемые в строках подключения (в так называемых URL LDAP):

  • OU — Organization Unit — организационный блок (организационная единица или подразделение), которая содержит такие объекты, как пользователи, контакты, группы и др.
  • CN — Common Name — общее (относительное) имя. Пользователь, контакт, группа или другой объект, который как правило не имеет дочерних объектов.
  • DC — Domain Component – компонент доменного имени.

Для связывания (определения) объекта с помощью технологии ADSI можно использовать два способа:

  • «Развёрнутая» форма:
  • «Сокращённая» форма:

Как видно из примеров, строки связывания в разных формах отличаются в основном направлением переходов по иерархии (вверх или вниз по иерархии). Примечание: порт можно указать только во второй форме, порт по умолчанию — 389.

Имя сервера (контроллера домена) и номер порта можно не указывать. В этом случае программные модули ADSI автоматически найдут контроллер домена для указанного вами домена и произведут к нему подключение с параметрами по умолчанию. При этом используются те же механизмы, что и при входе клиентского компьютера в сеть (такой вариант называется привязкой без сервера — serverless binding). Например:

Для просмотра атрибутов записи каталога можно связать запись, а затем использовать метод Get. Если сервер LDAP разрешает анонимный доступ к каталогу, можно использовать следующий подход:

Если сервер LDAP не разрешает анонимный доступ к каталогу, можно использовать следующий подход:

Для просмотра архитектуры существующего каталога LDAP используйте скрипт, приведённый чуть ниже. Изменяя отправную точку (startingPoint), можно просмотреть различные части каталога:

Рекурсивный просмотр архитектуры существующего каталога LDAP, вглубь от указанной отправной точки (startingPoint):

Специальный объект RootDSE (Root Directory Service Entry) автоматически вернёт имя того домена, к которому принадлежит данный компьютер:

Объект RootDSE предоставляет различную информацию о возможностях LDAP-сервера:

Поиск объектов в каталоге

В ADSI реализовано несколько технологий поиска. Основная технология связана с применением для этой цели возможностей объектной модели ADO. При этом подключение к службе каталогов производится при помощи объекта ADODB.Connection, формулировка текста запроса и его выполнение — при помощи объекта ADODB.Command, а результаты запроса возвращаются в виде обычного объекта ADODB.Recordset. Объектная модель ADO полностью поддерживает OLE Automation, поэтому эту технологию можно использовать из любых языков программирования, которые поддерживают OLE Automation, в том числе VBA, VBScript, JScript и т.п. Остальные технологии поиска ADSI могут использоваться в основном только из программ на языке C++, и здесь рассматриваться не будут.

Запросы к службам каталогов в ADSI могут выполняться на двух диалектах — диалекте LDAP (фильтры запроса определяются в соответствии с правилами синтаксиса запросов LDAP по RFC 2254) и диалекте SQL (фильтры запроса определяются в соответствии со стандартом ANSI SQL с учетом особенностей работы со службами каталогов).

Пример запроса на диалекте LDAP:

Первая часть запроса — базовое отличительное имя (base distinguished name) — использует синтаксис вида . Отличительное имя должно принадлежать контейнерному объекту, в котором производится поиск (обычно домену, сайту или организационному подразделению).

Вторая часть запроса (в примере — (object .

Третья часть запроса (в примере — AdsPath, cn) является набором атрибутов (столбцов), которые будут возвращаться в ходе выполнения запроса. Если необходимо вернуть несколько атрибутов для объекта, наименования атрибутов должны быть разделены запятыми.

Четвертая часть запроса (в примере — subTree) задаёт диапазон поиска. Возможные значения:

  • base — поиск производится только по одному объекту, указанному в первой части запроса. Всегда возвращается либо один объект, либо пустой набор объектов. По сути это проверка существования объекта;
  • onelevel — поиск производится только по непосредственным дочерним объектам контейнерного объекта, указанного в первой части запроса. Поиск по вложенным объектам более низких уровней производится не будет. В поиск не попадёт и сам контейнерный объект;
  • subtree — поиск будет производиться по всем объектам вниз по иерархии — глубокий поиск (deep search). В поиск при этом не попадает сам контейнерный объект.

Для поисковых фильтров LDAP предусмотрено два варианта синтаксиса. Первый вариант использует только один фильтр:

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

Операторы, используемые в фильтрах поиска LDAP:

=

= Равно.
Приблизительно равно.
= Больше или равно.
& И
| ИЛИ
! НЕ
* Групповой подстановочный символ (любое количество символов).

(object > Для атрибута objectClass может быть использовано любое значение (т.е. вернутся все объекты).
(&(objectCategory=person)(object > Несколько фильтров объединены при помощи оператора «И». Вернутся только те объекты, у которых одновременно: атрибут objectCategory имеет значение «person», атрибут objectClass — значение «user», атрибут cn — значение, не равное «andy».
(&(objectCategory=person)(object > Вернутся все объекты, у которых значение атрибута objectCategory равно «Person», objectClass — «contact», а значение атрибута sn равно либо «Lee», либо «Smith».

Для проверки наличия определенных флагов в атрибутах объектов применяется синтаксис с использованием так называемых объектных идентификаторов (object identifiers, OIDs). LDAP поддерживает два OID:

  • 1.2.840.113556.1.4.803 — совпадение будет признано только тогда, когда совпадут все биты в сравниваемых значениях (bitwise AND);
  • 1.2.840.113556.1.4.804 — совпадение будет признано в случае, если совпадет любой бит в сравниваемых значениях (bitwise OR).

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

Здесь число 2147483648 — это десятичное представление числа 0x80000000, которое соответствует флагу ADS_GROUP_TYPE_SECURITY_ENABLED в атрибуте groupType.

Некоторые символы в поисковых фильтрах являются зарезервированными и в значениях атрибутов должны заменяться на специальные последовательности:

* \2a
( \28
) \29
\ \5c
NUL \00
/ \2f

Пример скрипта, разыскивающего все объекты в домене, в имени которых используется прямой слеш:

Пример запроса на диалекте языка SQL к службе каталогов:

В разделе SELECT перечисляются атрибуты объектов Active Directory, которые будут возвращаться запросом. В результатах запроса они будут представлены полями объекта ADODB.Recordset. Несколько имен атрибутов должны разделяться запятыми. Вместо списка атрибутов можно использовать ключевое слово ALL или звездочку (*). Эти значения определяют, что должны вернуться все атрибуты.

В разделе FROM указывается отличительное имя базового объекта поиска (то есть того контейнерного объекта, начиная с которого будет производиться поиск). Этот путь обязательно должен быть заключен в одинарные кавычки.

В разделе WHERE указываются условия, поисковые фильтры, которые используются для выбора объектов при поиске. Синтаксис соответствует стандартам языка SQL.

В разделе ORDER BY указывается атрибут объекта службы каталогов, по значению которого будет производиться сортировка возвращаемых результатов. Active Directory поддерживает сортировку только по одному атрибуту. Однако можно указать порядок сортировки при помощи ключевых слов ASC и DESC (по умолчанию используется ASC).

Оператор JOIN в запросах к Active Directory не поддерживается (если запрос производится из SQL Server, а Active Directory используется в качестве внешнего источника данных, то можно производить JOIN между таблицами SQL Server и информацией, возвращаемой Active Directory).

Пример скрипта, разыскивающего все объекты в домене, в имени которых используется прямой слеш:

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

Классы ADSI

Интерфейс IADs определяет набор базовых свойств и методов для любого объекта ADSI:

ADsPathЭто свойство возвращает отличительное имя объекта в Active Directory. Значение этого свойства может выглядеть по разному в зависимости от того, как именно получена ссылка на данный объект. Значение этого свойства выглядит как путь LDAP и может использоваться для получения этого объекта функцией GetObject.
ClassЭто свойство возвращает имя класса Active Directory, к которому принадлежит данный объект, согласно схеме Active Directory. Например, «domainDNS» или «user».
GUIDГлобальный уникальный идентификатор объекта Active Directory.
NameЭто свойство возвращает относительное отличительное имя данного объекта (relative distinguished name, RDN), например, «cn=TestUser1».
ParentЭто свойство возвращает значение свойства ADsPath родительского контейнера данного объекта.
SchemaЭто свойство возвращает ADsPath объекта в схеме Active Directory.
GetInfo()Этот метод позволяет заново получить в оперативную память компьютера, на котором создан объект ADSI, информацию о значениях свойств этого объекта с контроллера домена.
SetInfo()Этот метод записывает информацию об изменениях, произведённых с объектом ADSI в оперативной памяти, на контроллер домена, делая таким образом изменения постоянными. Фактически любая операция по внесению изменений в Active Directory средствами ADSI завершается вызовом метода SetInfo(). Контроллер домена по разным причинам может отказаться сохранять внесенные изменения (например, настроенный для пользователя пароль не соответствует парольной политике домена), поэтому лучше всего при вызове этого метода позаботиться об обработке ошибок.
Get()Этот метод, как и метод GetInfo(), обновляет кэш свойств, скачивая значения заново с контроллера домена. Однако GetInfo() обновляет значения всех свойств, а Get() — только указанного. Имя этого свойства передается в качестве параметра.
GetEx()Этот метод делает практически то же, что и метод Get(). Если для какого-то свойства предусмотрено единственное значение (например, строковое), метод Get() вернет просто это строковое значение. Если для этого свойства предусмотрено несколько строковых значений, метод Get() вернет массив строковых значений. Метод GetEx() всегда возвращает массив значений типа Variant, вне зависимости от того, было ли для свойства настроено одно значение или несколько. Поэтому метод GetEx() очень удобно использовать в ситуации, когда неизвестно, вернется одно значение или несколько.
GetInfoEx()Этот метод аналогичен методу GetEx(). Отличием является то, что метод GetEx() принимает в качестве параметра имя единственного свойства, а метод GetInfoEx() — массив с именами свойств. Соответственно, этот метод может обновить значения не для одного свойства, а для набора свойств.
Put()Этот метод позволяет присвоить значение свойству в кэше свойств для объекта ADSI в оперативной памяти. Чаще всего он используется в ситуации, когда объект ADSI только что создан и ещё не сохранен на контроллере домена (в этом случае прямое присвоение значения свойству вызовет ошибку). Потом при вызове метода SetInfo() настроенные значения свойств будут сохранены вместе с объектом в базе данных Active Directory. Обычно метод Put() используется только для присвоения значений обязательным свойствам, без которых сохранение объекта невозможно. Далее вызывается метод SetInfo(), и работа со значениями свойств идёт уже обычным порядком.
PutEx()Этот метод предназначен для тех же целей, что и метод Put(), но в качестве ещё одного параметра принимает информацию о том, что именно нужно сделать с указанным значением свойства (далее указаны значения перечисления ADS_PROPERTY_OPERATION_ENUM):

  • ADS_PROPERTY_CLEAR (1) — очистить данное свойство, убрав все его значения;
  • ADS_PROPERTY_UPDATE (2) — обновить значение для данного свойства, заменив его указанным новым значением;
  • ADS_PROPERTY_APPEND (3) — новое значение должно добавиться к уже существующим (если свойство поддерживает набор значений);
  • ADS_PROPERTY_DELETE (4) — указанное значение должно быть удалено из набора значений для данного свойства.

Интерфейс IADsContainer обеспечивает набор свойств и методов для контейнерных объектов Active Directory (в основном — объектов организационных подразделений и доменов):

Filter Это свойство позволяет настроить фильтр для элементов в данном контейнере. Элементы, которые не соответствуют данному фильтру, при работе с контейнером (переборе его элементов) будут игнорироваться. Это свойство в качестве значения принимает массив значений типа Variant с именами классов объектов Active Directory согласно схеме (например, «user», «computer» и т.п.).
Create() Этот метод позволяет создать новый элемент в данном контейнере. Он принимает два обязательных параметра: имя класса создаваемого объекта (например, «user’, «group», «organizationalUnit», «computer» и т.п.) и относительное отличительное имя для данного объекта (точно в таком же формате, в котором это имя возвращается при помощи свойства IADs.Name). Этот метод возвращает ссылку на созданный объект. Затем можно присвоить значение свойствам данного объекта (лучше при помощи метода IADs.Put(), чтобы гарантировать отсутствие ошибок) и сохранить созданный объект при помощи метода SetInfo().
Delete() Этот метод позволяет удалить элемент из данного контейнерного объекта. Он принимает в качестве параметров имя класса и относительное отличительное имя удаляемого объекта. Если класс в Active Directory уже отключен, вместо имени класса можно передать NULL.
GetObject() Этот метод позволяет вернуть ссылку на элемент в контейнерном объекте. В качестве параметров этот элемент принимает класс объекта и его относительное отличительное имя.
MoveHere() Этот метод позволяет переместить какой-либо элемент в данный контейнерный объект. В качестве параметров принимает полное отличительное имя существующего объекта и его новое относительное отличительное имя. Возвращает ссылку на интерфейс IADs для перемещённого объекта, которую можно использовать для дальнейшей настройки свойств объекта. Если переместить объект в тот же контейнер, в котором он уже находится, указав при помощи второго параметра новое имя, тем самым можно переименовать объект. При помощи этого метода можно перемещать в данный контейнерный объект как объекты из того же домена, так и объекты из других доменов данного леса. Однако для перемещения объектов из других доменов необходимо, чтобы домен назначения работал по крайней мере в режиме Windows 2000 Native, и перемещать можно только конечные объекты (например, объекты пользователей) или пустые контейнерные объекты (например, организационное подразделение, в котором нет никаких вложенных объектов). При перемещении объекта между доменами для объекта создается новый SID, а старый сохраняется при помощи атрибута SIDHistory. Явно предоставленные разрешения для пользователя при этом сохраняются, но теряется его членство в глобальных группах. Для перемещаемых объектов пользователей назначенные им пароли сохраняются.

Интерфейс IADsPropertyList предназначен для работы со свойствами объектов Active Directory. В принципе, работать со свойствами можно и без него (например, при помощи метода Put), однако средствами IADsPropertyList можно получить информацию о всех свойствах конкретного объекта, их типе и т.п. Использовать свойства и методы этого интерфейса можно только после того, как для соответствующего объекта Active Directory будет вызван метод IADs.GetInfo() или GetInfoEx(), или Get(), или GetEx().

Пример получения информации об имени, типе и значениях всех свойств объекта:

Свойства и методы интерфейса IADsPropertyList:

PropertyCount Это свойство интерфейса IADsPropertyList возвращает информацию о количестве свойств, предусмотренных для данного объекта.
GetPropertyItem() Этот метод позволяет вернуть свойство по его имени. В качестве параметров этот метод принимает имя свойства и его тип в виде значения перечисления ADSTYPEENUM (в нём предусмотрено 28 значений). Если тип свойства вам не известен, можно передать специальное значение ADSTYPE_UNKNOWN (26). Этот метод возвращает информацию в виде ссылки на объект IADsPropertyEntry, который можно использовать для получения информации о значении свойства.
Item() Этот метод отличается от метода GetPropertyItem() тем, что позволяет получить свойство (то есть объект IADsPropertyEntry) по имени или номеру. Тип свойства передавать при этом не надо.
Next() Этот метод позволяет вернуть следующее свойство в виде объекта IADsPropertyEntry. Обычно он используется для перебора всех свойств какого-либо объекта.
PurgePropertyList() Этот метод можно считать обратным методу IADs.GetInfo() — он очищает кэш свойств, удаляя из него информацию для всех свойств данного объекта. При этом сам объект Active Directory никак не затрагивается — этот метод работает только с представлением данного объекта в памяти. Обычно он используется, чтобы высвободить память на компьютере, на котором производятся операции с объектами Active Directory средствами ADSI.
PutPropertyItem() Этот метод позволяет присвоить новое значение свойству объекта Active Directory. По своим возможностям этот метод очень похож на метод Put() интерфейса IADs, и после его вызова необходимо вызвать метод IADs.SetInfo(). В качестве параметра метод PutPropertyInfo() принимает объект PropertyEntry, в котором должно быть задано новое значение для свойства.
Reset() Этот метод позволяет вернутся на начало списка свойств после вызова метода Next(), чтобы вернуться к исходному состоянию.
ResetPropertyItem() Этот метод позволяет удалить указанное свойство из кэша свойств. Этому методу можно передать как имя свойства, так и его номер. Этот метод используется только для экономии оперативной памяти, на сам объект Active Directory он никак не влияет.
Skip() Этот метод позволяет переместить курсор для списка свойств на указанное количество позиций. В качестве параметра этот метод принимает количество позиций, на которое следует переместиться. Обычно этот метод используется вместо метода Next() или вместе с ним для сокращения количества выполняемых операций.

Интерфейс IADsPropertyEntry предназначен для работы со значением (или значениями) свойства объекта Active Directory. Он может использоваться как для получения информации о значении свойства, так и для назначения свойству нового значения или значений. Свойства и методы:

NameИмя свойства, которое всегда соответствует наименованию соответствующего атрибута для объекта Active Directory согласно схеме.
ADsTypeТип свойства в соответствии со значением перечисления ADSTYPEENUM.
ControlCodeПозволяет определить тип операции, выполняемой со свойством. Возможные значения (перечисление ADS_PROPERTY_OPERATION_ENUM):

  • ADS_PROPERTY_CLEAR (1) — очистить объект свойства, убрав все настроенные для него ранее значения свойств;
  • ADS_PROPERTY_UPDATE (2) — заменить существующее значение свойства другим значением;
  • ADS_PROPERTY_APPEND (3) — добавить указанное значение к уже существующим;
  • ADS_PROPERTY_DELETE (4) — убрать ранее настроенное конкретное значение.

ValuesОпределяет значения для свойства. Свойство Values работает с массивом объектов типа Variant. Любой из объектов типа Variant, который находится в данном массиве, должен обязательно реализовывать интерфейс IADsPropertyValue.

Интерфейсы IADsPropertyValue и IADsPropertyValue2 используются непосредственно для работы со значениями свойств. Каждому объекту IADsPropertyValue соответствует конкретное значение свойства. Массив значений (то есть массив объектов IADsPropertyValue) передаётся при помощи свойства Values объекта IADsPropertyEntry.

Свойства и методы интерфейса IADsPropertyValue:

ADsType Это свойство позволяет определить тип данных для свойства в соответствии с набором значений, предусмотренных перечислением ADSTYPEENUM.
Clear() Этот метод позволяет очистить все значения, заданные ранее для объекта IADsPropertyValue.
DNString
CaseExactString
CaseIgnoreString
PrintableString
NumericString
Boolean
Integer
OctetString
UTCTime
LargeInteger
SecurityDescriptor
Эти свойства предназначены для работы с атрибутами соответствующих типов (в соответствии с перечислением ADSTYPEENUM). Попытка обращения к свойству несоответствующего типа вернёт ошибку. Если вам потребовалось выполнить какую-либо операцию со значением такого типа, который не представлен в этой таблице, интерфейс IADsPropertyValue для этой цели использовать будет невозможно. Вместо этого вам придётся воспользоваться интерфейсом IADsPropertyValue2.

Методы интерфейса IADsPropertyValue2:

GetObjectProperty() Этот метод позволяет получить значение атрибута объекта Active Directory. Он принимает единственный параметр: тип атрибута в соответствии с перечислением ADSTYPEENUM и возвращает значение этого атрибута при помощи типа данных Variant.
PutObjectProperty() Этот метод позволяет задать значение для атрибута объекта Active Directory. В качестве параметров он принимает тип атрибута в соответствии с перечислением ADSTYPEENUM и само значение (как Variant).

Значения перечисления ADSTYPEENUM:

ADSTYPE_INVALID
ADSTYPE_DN_STRING 1
ADSTYPE_CASE_EXACT_STRING 2
ADSTYPE_CASE_IGNORE_STRING 3
ADSTYPE_PRINTABLE_STRING 4
ADSTYPE_NUMERIC_STRING 5
ADSTYPE_BOOLEAN 6
ADSTYPE_INTEGER 7
ADSTYPE_OCTET_STRING 8
ADSTYPE_UTC_TIME 9
ADSTYPE_LARGE_INTEGER 10
ADSTYPE_PROV_SPECIFIC 11
ADSTYPE_OBJECT_CLASS 12
ADSTYPE_CASEIGNORE_LIST 13
ADSTYPE_OCTET_LIST 14
ADSTYPE_PATH 15
ADSTYPE_POSTALADDRESS 16
ADSTYPE_TIMESTAMP 17
ADSTYPE_BACKLINK 18
ADSTYPE_TYPEDNAME 19
ADSTYPE_HOLD 20
ADSTYPE_NETADDRESS 21
ADSTYPE_REPLICAPOINTER 22
ADSTYPE_FAXNUMBER 23
ADSTYPE_EMAIL 24
ADSTYPE_NT_SECURITY_DESCRIPTOR 25
ADSTYPE_UNKNOWN 26
ADSTYPE_DN_WITH_BINARY 27
ADSTYPE_DN_WITH_STRING 28

В интерфейсе IADsDomain реализованы свойства, обращаться к которым можно только через провайдер WinNT. Пример работы со свойствами объекта домена приведён в статье Введение в Active Directory Service Interface (ADSI): провайдер WinNT. Впрочем, можно работать через значения атрибутов и интерфейсы IADsPropertyList, IADsPropertyEntry, IADsPropertyValue.

Объект организационного подразделения реализует стандартные интерфейсы IADs, IADsContainer, IADsPropertyList, а также специализированный интерфейс IADsOU. Свойства IADsOU (например, BusinessCategory, Description, FaxNumber, LocalityName, TelephoneNumber) достаточно очевидны.

Объект группы поддерживает стандартные интерфейсы IADs и IADsPropertyList, а также специализированный интерфейс IADsGroup. Многие важные свойства групп (например, тип группы) через данный интерфейс недоступны и работать с ними нужно средствами интерфейсов IADsPropertyList, IADsPropertyEntry, IADsPropertyValue. Для атрибута groupType (тип группы) используются значения:

  • 1 — системная группа (такие группы создаются автоматически при создании домена, самостоятельно создать такую группу нельзя);
  • 2 — глобальная группа (по умолчанию);
  • 4 — доменная локальная группа;
  • 8 — универсальная группа;
  • 0x80000000 — группа безопасности. Это значение используется только в сочетании с другими значениями.

Свойства и методы интерфейса IADsGroup:

Description Это свойство представляет текстовое описание для данной группы.
Add() Этот метод позволяет добавить объект Active Directory (чаще всего учетную запись пользователя) в группу. Ему можно передавать путь в формате AdsPath или SID объекта в различных форматах.
IsMember() Этот метод позволяет проверить, является ли объект Active Directory членом данной группы и возвращает True или False. Учитывается только непосредственное членство: если, например, объект пользователя входит в глобальную группу, которая в свою очередь входит в локальную, то проверка членства для пользователя в локальной группе вернет False. Проверка может быть произведена только для объектов того же домена. Метод принимает в качестве параметра путь AdsPath или SID объекта (для провайдера WinNT проверка по SID не поддерживается).
Members() Этот метод возвращает коллекцию объектов Active Directory, которые являются членами данной группы (как Variant).
Remove() Метод, обратный методу Add(), позволяет удалить объект из группы. Этот метод принимает параметры в том же формате, что и метод Add().

Объект пользователя в ADSI поддерживает свойства и методы стандартных интерфейсов IADs и IADsPropertyList. Также для него реализован специализированный интерфейс IADsUser, в который сведены свойства для доступа к наиболее часто используемым атрибутам. Для объекта пользователя в Active Directory предусмотрено огромное количество атрибутов (несколько сотен), и для многих из них доступ возможен только через интерфейсы IADsPropertyList, IADsPropertyEntry, IADsPropertyValue (но не через IADsUser). Большая часть свойств (Department, Division, EmailAddress, FaxNumber, FirstName, FullName и т.п.) очевидна и комментариев не требует. Некоторые не самые очевидные свойства и методы:

AccountDisabled Это свойство определяет, отключена ли учетная запись. По умолчанию объект пользователя создается в отключенном состоянии. Для этого свойства используются значения True и False. Однако для атрибута UserAccountControl объекта пользователя в Active Directory, который соответствует этому свойству, предусмотрено несколько десятков значений. Значению False свойства AccountDisabled соответствует значение атрибута UserAccountControl 544, а значению True (то есть учетная запись отключена) — 546.
AccountExpirationDate Время и дата, когда учётная запись автоматически отключится. Обычно используется для временных сотрудников.
BadLoginAddress Информация о компьютере, с которого была произведена последняя попытка неудачного входа (с неверным паролем) для данной учётной записи. Эту информацию (свойство доступно только на чтение) можно использовать для обнаружения вторжений.
BadLoginCount Информация о количестве неудачных попыток входа (с неверным паролем) после последнего сброса счётчика.
GraceLoginsAllowed Информация о количестве входов в сеть, которые пользователь может произвести после того, как срок его пароля истек.
GraceLoginsRemaining Количество оставшихся входов в сеть для данного пользователя (когда срок действия пароля закончился).
IsAccountLocked Заблокирована ли учетная запись пользователя после превышения порогового значения неверных попыток входа. Это свойство доступно и на чтение, и на запись, поэтому его можно использовать для разблокирования учетной записи.
LastFailedLogin Информация о дате и времени последней неудачной попытки (по причине неверного пароля) входа пользователя. Можно использовать для расследования попыток вторжения.
LastLogin Информация о дате и времени последнего входа в сеть.
LastLogoff Информация о последнем выходе пользователя из сети (если этот выход был произведен корректно).
MaxLogins Информация о максимальном количестве пользователей, которые могут одновременно входить в сеть от имени данной учетной записи.
PasswordExpirationDate Дата и время, когда срок действия пароля данного пользователя истечет.
PasswordLastChanged Дата и время последнего изменения пароля.
PasswordMinimumLength Минимальная длина пароля в символах.
PasswordRequired Это свойство (доступное и на чтение, и на запись) позволяет определить, обязателен ли пароль для данного пользователя.
RequireUniquePassword Это свойство (доступное и на чтение, и на запись) позволяет определить, распространяются ли на данную учетную запись требования по уникальности пароля (то есть будет ли к нему применяться проверка истории паролей).
ChangePassword() Этот метод позволяет поменять пароль пользователя. В качестве параметров этот метод принимает старый пароль и новый пароль. Обычно этот метод используется для того, чтобы создать свой собственный интерфейс, при помощи которого пользователь сможет менять себе пароль.
SetPassword() Этот метод предназначен для администраторов и позволяет поменять пароль, не зная старого. В качестве параметра он принимает только новый пароль.
Groups() Этот метод позволяет вернуть коллекцию объектов групп, в которые входит данный пользователь. При этом системные группы (такие, как Domain Users) не учитываются.

Примеры

Просмотр атрибутов объекта:

Модификация атрибутов объекта:

Отображение всех объектов пользователей, групп или компьютеров указанного контейнера:

Просмотр всех обязательных свойств класса объекта (важно для создания объектов):

Что такое код ldap_next_attribute

уМХЦВБ ЛБФБМПЗБ ( Directory Service )љ— ЬФП РТПЗТБННОЩК ЛПНРМЕЛУ ДМС ИТБОЕОЙС Й ЛБФБМПЗЙЪБГЙЙ ЙОЖПТНБГЙЙ. рП УЧПЕК УХФЙ ЬФП ПЮЕОШ РПИПЦЕ ОБ ПВЩЮОХА ВБЪХ ДБООЩИ, ОП У “ХЛМПОПН” УЛПТЕЕ ОБ ЮФЕОЙЕ ДБООЩИ, ОЕЦЕМЙ ОБ ЙИ ДПВБЧМЕОЙЕ ЙМЙ НПДЙЖЙЛБГЙА. пВЩЮОП УМХЦВБ ЛБФБМПЗБ ВБЪЙТХЕФУС ОБ ЛМЙЕОФ-УЕТЧЕТОПК БТИЙФЕЛФХТЕ. пДОБ ЙЪ ОБЙВПМЕЕ ЙЪЧЕУФОЩИ ФБЛЙИ УЙУФЕНљ— ЬФП DNS ( Domain Name Service ): DNS-УЕТЧЕТ РТПЙЪЧПДЙФ ЧЪБЙНОХА “ФТБОУМСГЙА” ЙНЈО НБЫЙО Й ЙИ IP-БДТЕУПЧ. дТХЗЙЕ НБЫЙОЩ Ч УЕФЙ НПЗХФ ПВТБЭБФШУС Л ФБЛПНХ УЕТЧЕТХ ЪБ ЙОЖПТНБГЙЕК П УППФЧЕФУФЧЙЙ ЙНЕОЙ Й БДТЕУБ. пДОБЛП ЬФП ПЮЕОШ РТПУФПК РТЙНЕТ ЛБФБМПЗЙЪБГЙЙ ЙОЖПТНБГЙЙ. пВЯЕЛФЩ Ч ФБЛПК ВБЪЕ ЙНЕАФ ПЗТБОЙЮЕООПЕ ЛПМЙЮЕУФЧП БФТЙВХФПЧљ— ФБЛЙИ ЛБЛ ЙНС, БДТЕУ Й ЕЭЈ ОЕУЛПМШЛП ДПРПМОЙФЕМШОЩИ РБТБНЕФТПЧ. тБЪХНЕЕФУС, УМХЦВБ ЛБФБМПЗБ ЛБЛПЗП-ОЙВХДШ РТЕДРТЙСФЙС ВХДЕФ УПДЕТЦБФШ ВПМЕЕ ТБЪОППВТБЪОЩЕ ДБООЩЕ Й ЙНЕФШ ЗПТБЪДП ВПМЕЕ УМПЦОХА УФТХЛФХТХ.

ч ПВЭЕН УМХЮБЕ, УМХЦВБ ЛБФБМПЗБ ДПМЦОБ РТЕДПУФБЧМСФШ РТПУФПК, ГЕОФТБМЙЪПЧБООЩК ДПУФХР Л ДБООЩН, ЛПФПТЩЕ НПЗХФ ЙУРПМШЪПЧБФШУС ТБЪМЙЮОЩНЙ РТЙМПЦЕОЙСНЙ. рТПФПЛПМ, РП ЛПФПТПНХ НПЗМБ ВЩ ТБВПФБФШ ФБЛБС УМХЦВБ, ВЩМ ТБЪТБВПФБО Ч ISO ( International Standartization Organization ), РПМХЮЙМ ОПНЕТ X.500 Й ОБЪЩЧБМУС DAP ( Directory Access Protocol ). ч УППФЧЕФУФЧЙЙ У ЬФЙН РТПФПЛПМПН МАВПЕ РТЙМПЦЕОЙЕ НПЦЕФ РПМХЮЙФШ ДПУФХР Л ЙОЖПТНБГЙЙ Ч ЛБФБМПЗЕ. фБН ЦЕ ВЩМБ РТЕДМПЦЕОБ ЗЙВЛБС Й МЕЗЛП ТБУЫЙТСЕНБС ЙОЖПТНБГЙПООБС УФТХЛФХТБ ЛПФПТБС РПЪЧПМСМБ ИТБОЙФШ Ч РТЙОГЙРЕ МАВПК ФЙР ДБООЩИ. л УПЦБМЕОЙА, X.500 ЙНЕМ Й ТСД ПЗТБОЙЮЕОЙК, ФБЛЙИ ЛБЛ ЪБЧЙУЙНПУФШ ПФ ЛПННХОЙЛБГЙПООПЗП ХТПЧОС, ЛПФПТЩК ОЕ СЧМСМУС УФБОДБТФОЩН РТПФПЛПМПН TCP Й ЪБРХФБООПУФШ ФТЕВПЧБОЙК Л РТБЧЙМБН ЙНЕОПЧБОЙС ПВЯЕЛФПЧ. ч ТЕЪХМШФБФЕ ТЕЫЕОЙЕ ОБ ВБЪЕ ЬФПЗП РТПФПЛПМБ УФБОПЧЙМПУШ ПЮЕОШ ДПТПЗЙН РТЙ ПВУМХЦЙЧБОЙЙ.

рПЪЦЕ РПСЧЙМУС РТПФПЛПМ LDAP ( Lightweight Directory Access Protocol ), ЛПФПТЩК РПЪЧПМЙМ ТЕБМЙЪПЧБФШ ДПУФХР РП TCP/IP Й НПЗ МЕЗЛП ТБУЫЙТСФШУС. ч ТЕЪХМШФБФЕ РПСЧЙМПУШ ТЕЫЕОЙЕ, РПЪЧПМСАЭЕЕ ПТЗБОЙЪПЧБФШ УМХЦВХ ЛБФБМПЗБ ОБ РТЕДРТЙСФЙЙ МАВПЗП НБУЫФБВБ.

уЕЗПДОС УХЭЕУФЧХЕФ ОЕУЛПМШЛП ТЕБМЙЪБГЙК ДБООПЗП РТПФПЛПМБ ПФ ТБЪМЙЮОЩИ ЖЙТН. оБЙВПМЕЕ ЙЪЧЕУФОЩЕ ЙЪ ОЙИљ— ЬФП Netscape Directory Service™, Microsoft Active Directory™, Novell Directory Service™. йЪ ОЕЛПННЕТЮЕУЛЙИ ТЕБМЙЪБГЙК LDAP ОБЙВПМШЫЕЕ ТБУРТПУФТБОЕОЙЕ РПМХЮЙМ РТПЕЛФ OpenLDAP . йНЕООП ЕЗП НЩ Й ВХДЕН ТБУУНБФТЙЧБФШ Ч ДБООПК ЗМБЧЕ, ИПФС ВПМШЫЙОУФЧП РПОСФЙК Й ПРТЕДЕМЕОЙК РТЙНЕОЙНП Й Л ДТХЗЙН ТЕБМЙЪБГЙСН УЕТЧЕТБ LDAP.

пУОПЧОЩЕ ФЕТНЙОЩ

дМС РПОЙНБОЙС ТБВПФЩ УМХЦВЩ ЛБФБМПЗБ ОЕПВИПДЙНП ХУЧПЙФШ ОЕУЛПМШЛП ЛМАЮЕЧЩИ ФЕТНЙОПЧ.

дБООЩЕ ЛБФБМПЗБ ИТБОСФУС Ч ЧЙДЕ ПВЯЕЛФПЧ ЙМЙ УХЭОПУФЕК (ПФ БОЗМ. entry ), УПУФПСЭЙИ ЙЪ УРЕГЙБМШОЩИ РПМЕК ОБЪЩЧБЕНЩИ БФТЙВХФБНЙ ( attributes ). оБВПТ БФТЙВХФПЧ, ЙИ УЙОФБЛУЙУ Й РТБЧЙМБ РПЙУЛБ ПРТЕДЕМСАФУС УИЕНПК ЛБФБМПЗБ ( scheme ). чУЕ ПВЯЕЛФЩ ЛБФБМПЗБ ЙДЕОФЙЖЙГЙТХАФУС УРЕГЙБМШОЩН БФТЙВХФПНљ— DN ( Distinguished Name ).

дБООЩЕ Ч ЛБФБМПЗЕ НПЦОП РТЕДУФБЧЙФШ Ч ЧЙДЕ ДТЕЧПЧЙДОПК УФТХЛФХТЩљ— DIT ( Directory Information Tree ). ьФП ПЮЕОШ РПИПЦЕ ОБ УФТХЛФХТХ, ЙУРПМШЪХЕНХА НОПЗЙНЙ ЖБКМПЧЩНЙ УЙУФЕНБНЙ. чЕТЫЙОПК ФБЛПЗП ДЕТЕЧБ СЧМСЕФУС ЛПТОЕЧПК ПВЯЕЛФ ( Root Entry ). DN ЛПТОЕЧПЗП ПВЯЕЛФБ ПДОПЧТЕНЕООП СЧМСЕФУС УХЖЖЙЛУПН ЛБФБМПЗБ .

лБЦДЩК РПУМЕДХАЭЙК ПВЯЕЛФ Ч УФТХЛФХТЕ ЛБФБМПЗБ ЙДЕОФЙЖЙГЙТХЕФУС ХОЙЛБМШОЩН ЪОБЮЕОЙЕН DN ЛПФПТЩК ПРЙУЩЧБЕФ РХФШ Л ПВЯЕЛФХ Ч ЛБФБМПЗЕ. еУМЙ РТПДПМЦЙФШ БОБМПЗЙА У ЖБКМПЧПК УЙУФЕНПК ФП DN МАВПЗП ПВЯЕЛФБ ФБЛ ЦЕ ЧЛМАЮБЕФ DN ЧУЕИ ПВЯЕЛФПЧ УФПСЭЙИ ЧЩЫЕ РП ЙЕТБТИЙЙ. пФМЙЮЙЕ Ч ДБООПН УМХЮБЕ ФПМШЛП Ч ФПН, ЮФП DN ЖПТНЙТХЕФУС ОЕ УМЕЧБ ОБРТБЧП, ЛБЛ РХФШ Л ЖБКМХ, Б ОБПВПТПФљ— УРТБЧБ ОБМЕЧП.

DN БДНЙОЙУФТБФПТБ ЛБФБМПЗБ ( Root Distinguished Name )љ— ЬФП УРЕГЙБМШОЩК ПВЯЕЛФ, ПРЙУЩЧБАЭЙК БДНЙОЙУФТБФПТБ ЛБФБМПЗБ. ьФПФ ПВЯЕЛФ ХЛБЪЩЧБЕФУС Ч ЛПОЖЙЗХТБГЙЙ УЕТЧЕТБ, ОП НПЦЕФ ПФУХФУФЧПЧБФШ Ч УБНПН ЛБФБМПЗЕ. л ФБЛПНХ ПВЯЕЛФХ ОЕ РТЙНЕОСАФУС УРЙУЛЙ ДПУФХРБ ( ACL ). ч ОЕЛПФПТЩИ ТЕБМЙЪБГЙСИ LDAP ФБЛПК ПВЯЕЛФ НПЦЕФ ОЕ ЙНЕФШ УХЖЖЙЛУБ.

вБЪБ РПЙУЛБ ( Base Distinguished Name )љ— ПВЯЕЛФ ЛБФБМПЗБ, ОБЮЙОБС У ЛПФПТПЗП РТПЙЪЧПДЙФУС РПЙУЛ. дЕМП Ч ФПН, ЮФП ОЕ ЧУЕЗДБ ЕУФШ ОЕПВИПДЙНПУФШ РТПЙЪЧПДЙФШ РПЙУЛ РП ЧУЕНХ ДЕТЕЧХ ЛБФБМПЗБ; ПЗТБОЙЮЙФШ ПВМБУФШ РПЙУЛБ НПЦОП ХЛБЪБОЙЕН Ч ЪБРТПУЕ ВБЪЩ РПЙУЛБ. рП ХНПМЮБОЙА ЬФПФ РБТБНЕФТ УППФЧЕФУФЧХЕФ УХЖЖЙЛУХ.

пВЯЕЛФЩ Й БФТЙВХФЩ

уЕТЧЕТЩ LDAP НПЗХФ РПУФБЧМСФШУС У ОЕУЛПМШЛЙНЙ ЧБТЙБОФБНЙ ВЬЛЕОДБ ( backend ). оБРТЙНЕТ, OpenLDAP ЙНЕЕФ ФБЛЙЕ ЧБТЙБОФЩ, ЛБЛ LDBMљ— УПВУФЧЕООЩК ЖПТНБФ ИТБОЕОЙС ДБООЩИ Ч ФЕЛУФПЧЩИ ЖБКМБИ; SHELLљ— ЙОФЕТЖЕКУ Л ВБЪЕ ДБООЩИ, ЙУРПМШЪХАЭЙК ЛПНБОДЩ UNIX; PASSWDљ— РТПУФЕКЫБС ВБЪБ, ЙУРПМШЪХАЭБС УФБОДБТФОЩЕ ЖБКМЩ /etc/passwd Й /etc/group; SQLљ— ЙОФЕТЖЕКУ Л МАВПК ВБЪЕ ДБООЩИ, ЙУРПМШЪХАЭЕК SQL.

дМС РТПГЕДХТ ЙНРПТФБ Й ЬЛУРПТФБ ДБООЩИ ЧУЕНЙ УЕТЧЕТБНЙ LDAP РПДДЕТЦЙЧБЕФУС ЕДЙОЩК ЖПТНБФ ПВНЕОБ ДБООЩНЙљ— LDIF. чПФ РТЙНЕТ ФБЛПЗП ЖБКМБ У ПРЙУБОЙЕН ДЧХИ ПВЯЕЛФПЧ:

пРЙУБОЙЕ ЛБЦДПЗП ПВЯЕЛФБ Ч ФБЛПН ЖБКМЕ ОБЮЙОБЕФУС У БФТЙВХФБ DN. уРЕГЙБМШОЩК БФТЙВХФ objectClass ХЛБЪЩЧБЕФ, Л ЛБЛЙН ЛМБУУБН ПФОПУЙФУС ДБООЩК ПВЯЕЛФ Й, УМЕДПЧБФЕМШОП, ЛБЛЙЕ БФТЙВХФЩ ПО НПЦЕФ ЙНЕФШ. ч ОБЫЕН УМХЮБЕ РТЙОБДМЕЦОПУФШ Л ЛМБУУХ top ПЪОБЮБЕФ, ЮФП ПВЯЕЛФ ПВСЪБФЕМШОП ДПМЦЕО ЙНЕФШ БФТЙВХФ objectClass, Б РТЙОБДМЕЦОПУФШ Л ЛМБУУХ organization РТЕДРПМБЗБЕФ ОБМЙЮЙЕ ОЕУЛПМШЛЙИ БФТЙВХФПЧ, ЙЪ ЛПФПТЩИ БФТЙВХФ o СЧМСЕФУС ПВСЪБФЕМШОЩН.

чФПТПК ПВЯЕЛФ ОБИПДЙФУС ОБ ПДОХ УФХРЕОШЛХ ОЙЦЕ РП ЙЕТБТИЙЙ Й РПЬФПНХ Ч ЕЗП DN ЧЛМАЮЈО DN ПВЯЕЛФБ ЧЕТИОЕЗП ХТПЧОС. ьФПФ ПВЯЕЛФ ПФОПУЙФУС Л ЛМБУУХ organizationalUnit Й РПЬФПНХ ЙНЕЕФ ПВСЪБФЕМШОЩК БФТЙВХФ ou.

нПЦОП ЪБНЕФЙФШ ЮФП ОЕЛПФПТЩЕ БФТЙВХФЩ (ДМС ЛПФПТЩИ ЬФП РТЙНЕОЙНП) НПЗХФ ЙНЕФШ ОЕУЛПМШЛП ЪОБЮЕОЙК. ч ДБООПН РТЙНЕТЕ БФТЙВХФ description ЙНЕЕФ ДЧБ ЪОБЮЕОЙС. б ЧПФ ДМС БФТЙВХФБ dn ДПРХУФЙНП ФПМШЛП ПДОП ЪОБЮЕОЙЕ.

лМБУУЩ, ИБТБЛФЕТЙЪХАЭЙЕ ПВЯЕЛФЩ, Й БФТЙВХФЩ, УПУФБЧМСАЭЙЕ ЛМБУУЩ, ПРЙУЩЧБАФУС УИЕНПК ВБЪЩ. еЈ РТЙНЕТ РТЙЧЕДЈО ОЙЦЕ.

ч ДБООПН ЖТБЗНЕОФЕ РТЙЧПДСФУС ПРЙУБОЙС ДЧХИ БФТЙВХФПЧ Й ПДОПЗП ЛМБУУБ. чПФ ЮФП ПЪОБЮБАФ ЬФЙ ЪБРЙУЙ:

бФТЙВХФ o (ЕЗП НПЦОП ФБЛЦЕ ОБЪЩЧБФШ organizationName) СЧМСЕФУС ТБУЫЙТЕОЙЕН БФТЙВХФБ name.

бФТЙВХФ descriptionљ— ЬФП УФТПЛБ ДМЙОПК ДП 1024 ВБКФ; РТЙ РПЙУЛЕ Ч ОЕК ТЕЗЙУФТ УЙНЧПМПЧ ОЕ ХЮЙФЩЧБЕФУС.

лМБУУ organization СЧМСЕФУС ТБУЫЙТЕОЙЕН ЛМБУУБ top Й ЙНЕЕФ ЕДЙОУФЧЕООЩК ПВСЪБФЕМШОЩК БФТЙВХФ o. лТПНЕ ФПЗП ЙНЕЕФУС ВПМШЫПЕ ЛПМЙЮЕУФЧП ОЕПВСЪБФЕМШОЩИ БФТЙВХФПЧ ФБЛЙИ ЛБЛ userPassword, businessAddress, street, postOfficeBox Й Ф.Д.

нОПЗП РПМЕЪОПК ЙОЖПТНБГЙЙ П УИЕНБИ НПЦОП ОБКФЙ РП УУЩМЛБН ОБ УБКФЕ OpenLDAP.

Илон Маск рекомендует:  Asp файл списка content linking
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL