• Архитектура активного каталога
  • Свойства
  • Концепции активного каталога
  • Объекты
  • Схема
  • Конфигурация
  • Домен активного каталога
  • Контроллер домена
  • Сайт
  • Дерево домена
  • Лес
  • Глобальный каталог
  • Репликация
  • Характеристики данных активного каталога
  • Схема
  • Управление активным каталогом
  • Active Directory Users and Computers
  • ADSI Edit
  • ADSI Viewer
  • Интерфейсы службы активного каталога (ADSI)
  • Программирование активного каталога
  • Классы в System.DirectoryServices
  • Связывание
  • Протокол
  • Имя сервера
  • Номер порта
  • Известное имя
  • Имя пользователя
  • Аутентификация
  • Связывание с помощью класса DirectoryEntry
  • Получение записей каталога
  • Свойства объектов пользователей
  • Коллекции объектов
  • Кэш
  • Обновление записей каталога
  • Создание новых объектов
  • Поиск в активном каталоге
  • Пределы поиска
  • Поиск объектов пользователей
  • Интерфейс пользователя
  • Получение именующего контекста схемы
  • Получение имен свойств класса пользователя
  • Поиск объектов User
  • Заключение
  • Глава 15

    Работа с активным каталогом

    Мы получаем активный каталог (Active Directory) как часть Windows 2000 Server. Активный каталог является службой каталога, где может храниться информация о пользователях, принтерах, службах и обычные данные. Exchange Server 2000 компании Microsoft интенсивно использует его для хранения общедоступных папок и другой информации. Мы также можем хранить в активном каталоге определяемые нами данные. В файловой системе каталог хранит файлы, телефонный каталог хранит телефонные номера и имена. Служба каталога делает доступной информацию в каталоге. С помощью Проводника можно, например, находить файлы.

    До появления ADS сервер Exchange мог использовать активный каталог для хранения своих объектов. Системным администраторам приходилось конфигурировать два идентификатора пользователя для одного человека: учетную запись пользователя в домене Windows NT, чтобы можно было зарегистрироваться в системе, и пользователя в Exchange Directory. Это было необходимо, так как для пользователей требовалась дополнительная информация (такая как адреса e-mail, телефонные номера и так далее), а данные о пользователях домена NT были нерасширяемыми, что не позволяло поместить туда требуемую информацию. Теперь системному администратору достаточно сконфигурировать только одного пользователя для человека в активном каталоге, данные объекта пользователя можно расширять, чтобы удовлетворить требованиям Exchange Server. Мы можем также расширить эту информацию.

    Рассмотрим менеджера проекта в большой компании, который ищет с помощью Active Directory разработчика, способного создать приложения с помощью C#. Было бы неплохо, если бы менеджер мог сделать простой запрос для получения списка всех разработчиков, удовлетворяющих его требованиям. Такую возможность предоставляет активный каталог, в котором объект пользователя дополняется списком навыков.

    Рассмотрим другой пример, где активный каталог может сыграть полезную роль: допустим, сконфигурирован используемый по умолчанию черно-белый принтер, но потребовалась цветная печать. Пользователь знает, что в пределах досягаемости имеется цветной принтер, который удовлетворяет требованиям, но какое у этого принтера имя? В диалоговом окне печати принтер можно выбирать из списка сотен странных имен типа Pikachu, Poliwag, Cloyster, Jynx, Staryu, которые когда-то выбрал системный администратор. Как выбрать правильный принтер? Давайте создадим решение, где пользователь может ввести такие требования, как расположение, двусторонняя печать и цвет для поиска принтера. Такая дополнительная информация о принтере также хранится в активном каталоге.

    С помощью среды .NET можно легко получить доступ и манипулировать данными в службе каталога с помощью классов из пространства имен

    System.DirectoryServices

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

    System.DirectoryServices
    , применяющиеся для службы каталогов Novell и Windows NT4.

    В этой главе мы рассмотрим:

    □ Архитектуру активного каталога.

    □ Чтение и изменение данных в активном каталоге.

    □ Поиск объектов в активном каталоге.

    Архитектура активного каталога

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

    Свойства

    Свойства активного каталога представлены в следующем списке:

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

    □ Активный каталог использует репликацию мультимастера. В противоположность доменам Windows NT 4, где контроллер первичного домена был мастером, при использовании активного каталога все серверы являются мастерами. Если первичный контроллер домена в домене Windows NT 4 выключен, ни один пользователь не может сменить пароль; системный администратор обновляет список пользователей только в том случае, когда первичный контроллер домена включен и работает. При использовании активного каталога обновление можно делать на любом сервере. Эта модель более масштабируема, так как обновления могут происходить на различных серверах одновременно. Недостатком такой модели является большая сложность репликации. Вопросы репликации мы рассмотрим позже.

    □ Топология репликации является гибкой, чтобы поддерживать репликации на медленных соединениях в WAN. Как часто должны реплицироваться данные, конфигурируется администраторами доменов.

    □ Активный каталог поддерживает открытые стандарты. LDAP (Lightweight Directory Access Protocol — легковесный протокол доступа к каталогу), является одним из стандартов, который может использоваться для доступа к данным в активном каталоге. LDAP является стандартом Интернета, который может использоваться для доступа ко множеству различных служб каталога. Так как LDAP также является интерфейсом программирования, то определен LDAP API, который можно применять для доступа к активному каталогу с помощью языка C. Предпочтительный интерфейс программирования компании Microsoft для служб каталога — это ADSI (Active Directory Service Interface — интерфейс служб активного каталога). Его конечно, нельзя назвать открытым стандартом. В противоположность LDAP API с помощью ADSI можно получить доступ ко всем свойствам активного каталога. Другим стандартом, который используется в активном каталоге, является Kerberos. Kerberos используется для аутентификации. Служба Kerberos в Windows 2000 может также применяться для аутентификации клиентов Unix. Это не работает в обратную сторону; серверы Unix Kerberos не могут аутентифицировать клиентов Windows 2000, так как компания Microsoft расширила протокол Kerberos для своего собственного использования.

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

    Объекты в каталоге являются строго типизированными. Это означает, что тип объектов точно определен, к объекту нельзя добавить ни одного не специфицированного атрибута. В схеме (Schema) определены типы объектов, а также части объектов (атрибуты). Атрибуты могут быть обязательными или необязательными.

    Концепции активного каталога

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

    Объекты

    В активном каталоге хранятся объекты. Объект — это что-то конкретное, например пользователь, принтер или общий сетевой ресурс. Объекты имеют обязательные и необязательные атрибуты, которые описывают объект. Примерами атрибутов объекта user (пользователь) являются фамилия, имя, адрес e-mail, телефонный номер и т. д.

    На следующем рисунке показаны: контейнерный объект Wrox Press, несколько объектов пользователей, контакт, принтер и объект группы пользователей:

    Схема

    Каждый объект является экземпляром класса в схеме (schema). Схема хранится среди объектов активного каталога. Мы должны различать

    classSchema
    и
    attributeSchema
    . В
    classSchema
    определяется тип объекта, а также данные о том, какие обязательные и необязательные атрибуты имеет объект.
    attributeSchema
    определяет, на что похож атрибут, и какой допустим для него синтаксис.

    Можно пользоваться собственными типами и атрибутами и добавить их к схеме. Помните, что новый тип схемы вам не удастся убрать из активного каталога. Можно пометить его как неактивный, чтобы новые объекты больше нельзя было создавать, но могут существовать объекты этого типа, поэтому невозможно удалить классы или атрибуты, которые определены в схеме. Windows 2000 Administrator не имеет достаточных полномочий для создания новых записей схемы, здесь нужен Windows 2000 Domain Enterprise Administrator.

    Конфигурация

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

    Домен активного каталога

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

    Users
    ,
    Computers
    и
    Books
    , могут хранить другие объекты:

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

    Books
    является предком для
    .NET
    и
    Java
    .
    Pro C#
    ,
    Beg C#
    , и
    ASP.NET
    являются объектами-потомками объекта .NET.

    Контроллер домена

    Один домен может иметь несколько серверов, каждый из которых хранит все объекты внутри домена. Не существует мастер-сервера, и все серверы интерпретируются одинаково; мы имеем модель с несколькими мастерами. Объекты реплицируются между серверами внутри домена.

    На следующем рисунке домен

    Wrox.com
    представлен треугольником. DC1 и DC2 являются двумя контроллерами домена для этого домена:

    Сайт

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

    На следующем рисунке показан один домен

    Wrox.com
    , который имеет несколько сайтов: Seattle, New York и Chicago. В каждом из этих сайтов выполняются по два контроллера домена.

    Дерево домена

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

    Домены, соединенные таким образом, называются деревом доменов. Домены в дереве доменов имеют непрерывное иерархическое пространство имен. Это означает, что имя домена-потомка добавляется к имени домена-предка. Между доменами устанавливаются доверительные отношения, использующие протокол Kerberos.

    На следующем рисунке показан корневой домен

    wrox.com
    , который является также доменом-предком доменов-потомков
    france.wrox.com
    и
    uk.wrox.com
    . Доверительные отношения задаются между доменами предком и потомком так, чтобы учетные записи одного домена можно было аутентифицировать в другом:

    Лес

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

    asptoday.com
    должен быть относительно независимым от домена
    wrox.com
    , но должно быть общее управление, а также возможность пользователям из домена
    asptoday.com
    иметь доступ к ресурсам из домена
    wrox.com
    и наоборот. С помощью леса можно создать доверительные отношения между множеством деревьев доменов.

    Глобальный каталог

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

    wrox.com
    , поиск продолжается в
    uk.wrox.com
    и
    france.wrox.com
    через медленные соединения, такой поиск может потребовать достаточно много времени.

    Чтобы ускорить поиск, все объекты копируются в глобальный каталог. Глобальный каталог реплицируется в каждый домен леса. Существует по крайней мере один сервер в каждом домене, содержащий глобальный каталог. По соображениям производительности и масштабируемости можно иметь в домене более одного сервера с глобальным каталогом. С помощью глобального каталога поиск по всем объектам может вестись на одном сервере.

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

    Глобальный каталог является для всех объектов кэш-памятью только для чтения. Каталог может применяться лишь для поиска; для выполнения обновлений должны использоваться контроллеры доменов.

    Не все атрибуты объекта хранятся в глобальном каталоге. Можно определить, должен ли он храниться там или нет. Решение о необходимости хранения атрибута в глобальном каталоге зависит от частоты использования его при поиске. Изображение пользователя бесполезно в глобальном каталоге, так как вряд ли поиск будет осуществиться по изображению. Вместо этого полезным добавлением для хранения был бы номер телефона. Атрибут можно также использовать для уточнения того, что он должен быть проиндексирован, с целью ускорения запроса этого атрибута.

    Репликация

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

    □ Конфигурируемое уведомление об изменении происходит внутри сайта по умолчанию каждые 5 минут, если изменяются некоторые атрибуты. Сервер, где происходит изменение, информирует все серверы по очереди с 30-секундным интервалом. В дальнейшем сервер может получать уведомление об изменении через 7 минут. По умолчанию уведомление об изменении между сайтами задается равным 180 минутам. Внутри- и межсайтная репликация может быть сконфигурирована на другие значения.

    □ Если никаких изменений не происходит, плановая репликация выполняется каждые 60 минут внутри сайта. Это служит гарантией того, что уведомление об изменении не будет пропущено.

    □ Для информации, чувствительной к безопасности, такой как блокирование учетной записи, может происходить немедленное уведомление.

    Изменения при репликации копируются в контроллеры доменов. Для каждого изменения атрибута записываются номер версии (USN — номер последовательности обновления) и отметка времени. Они используются для разрешения конфликтов, если обновления происходят в одном и том же атрибуте на различных серверах.

    Рассмотрим пример. Атрибут "мобильный телефон" пользователя Джона Доу имеет номер USN 47. Это значение уже реплицировано во все контроллеры доменов. Один системный администратор изменяет телефонный номер. Изменение происходит на сервере DC1, новый USN этого атрибута на сервере DC1 теперь будет 48, а остальные контроллеры доменов по прежнему имеют USN, равным 47. Если кто-то все еще читает атрибут, то он может считать старое значение, так как репликация еще не произошла на всех контроллерах доменов.

    Теперь может произойти редкий случай, когда другой администратор изменяет атрибут "телефонный номер", и здесь был выбран другой контроллер домена, так как этот администратор получил более быстрый ответ от сервера DC2. USN этого атрибута на сервере DC2 также изменяется на 48.

    Через заданный интервал происходит уведомление, так как USN атрибута изменился, и последний раз репликация происходила со значением USN, равным 47. С помощью механизма репликации теперь обнаруживается, что серверы DC1 и DC2 оба имеют USN, равный 48, для атрибута "номер телефона". Какой сервер будет победителем в действительности не имеет значения, но один сервер должен выиграть. Чтобы разрешить этот конфликт, используется отметка времени изменения. Так как изменение произошло позднее на DC2, то будет реплицировано значение, которое хранится в контроллере домена DC2.

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

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

    Характеристики данных активного каталога

    Активный каталог не заменяет реляционную базу данных или реестр. Какие же данные могут там хранится?

    □ В активном каталоге имеются иерархические данные. Мы можем иметь контейнеры, в которых опять же хранятся контейнеры, а также объекты. Сами контейнеры тоже являются объектами.

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

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

    □ Данные должны иметь разумный размер в связи с репликацией. Если размер данных равен 100Кбайт, то имеет смысл хранить их в каталоге, если данные изменяются только раз в неделю. Данные такого размера слишком велики, если они изменяются каждый час. Всегда помните о репликации — куда будут пересылаться данные и с какими интервалами времени. Если имеется большой объем данных, то можно поместить в активный каталог ссылку, а сами данные хранить в другом месте.

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

    Схема

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

    user
    является производным от класса
    organizationalPerson
    ,
    organizationalPerson
    является подклассом
    person
    , а базовым классом для всех является
    top
    . Класс
    classSchema
    , который определяет класс, описывает атрибуты с помощью атрибута
    systemMayContain
    .

    На диаграмме перечислены лишь немногие из всех значений

    systemMayContain
    только для того, чтобы передать идею конструкции. Можно легко найти все значения с помощью
    ADSIEdit
    . По корневому классу
    top
    видно, что каждый объект может иметь атрибуты общее имя (
    cn
    ),
    displayName
    ,
    objectGUID
    ,
    whenChanged
    и
    whenCreated
    . Класс
    Person
    является производным от
    top
    . Объект
    Person
    имеет также пароль
    userPassword
    и
    telephoneNumber
    .
    organizationalPerson
    является производным от
    Person
    . В дополнение к
    Person
    он имеет
    manager
    ,
    department
    ,
    company
    , a
    user
    имеет атрибуты, необходимые для регистрации в системе:

    Управление активным каталогом

    На самом деле мы не будем говорить об управлении активным каталогом. За управление отвечают системные администраторы Windows 2000, а мы хотим поговорить о программировании активного каталога. Однако рассмотрение некоторых инструментов управления может помочь понять, что такое активный каталог, какие данных в нем находятся, и что можно сделать программным путем.

    Системный администратор имеет множество инструментов для ввода новых данных, обновления данных и для конфигурирования активного каталога. С помощью инструмента Active Directory Users and Computers (Пользователи и компьютеры активного каталога) можно обновить данные о пользователях и ввести новых пользователей. Инструмент Active Directory Sites and Services используется для конфигурирования сайтов в домене и репликации между этими сайтами. ActiveDirectory Domains and Trusts может применяться для создания доверительных отношений между доменами в дереве. Редактором реестра для активного каталога, где можно просмотреть и отредактировать каждый объект, является ADSI Edit. В дополнение к инструментам системного администратора имеется инструмент в SDK платформы Microsoft: ADSI Viewer.

    Active Directory Users and Computers

    Утилита Active Directory Users and Computers является инструментом, который в основном служит системным администраторам для управления своими пользователями. Start|Programs|Administrative Tools|Active Directory Users and Computers: 

    С помощью этой утилиты можно добавлять новых пользователей, группы, контакты, организационные единицы, принтеры, общие папки, компьютеры, и модифицировать существующие. На изображении ниже можно видеть атрибуты, которые вводятся для объекта пользователь (user): офис, номера телефонов, адреса e-mail, web-страницы, информация об организации, адреса, группы и т.д., т.е. значительно больше информации, чем было возможно в домене NT 4:

    Утилита Active Directory Users and Computers может также использоваться на больших предприятиях с миллионами объектов. Не обязательно просматривать список с тысячами объектов, можно выбрать специальный фильтр, чтобы выводились только некоторые объекты. Можно также сделать запрос LDAP для поиска объектов на предприятии.

    ADSI Edit

    ADSI Edit является редактором реестра активного каталога. Эта утилита не устанавливается автоматически. На компакт-диске Windows 2000 Server можно найти каталог с именем

    Supporting Tools
    . Когда утилиты поддержки будут установлены, ADSI Edit можно будет найти в меню: Start|Programs|Windows 2000 Support Tools|Tools|ADSI Edit.

    Простая в использовании утилита Active Directory Users and Computers имеет фиксированный интерфейс пользователя для изменения атрибутов объектов пользователя. Мы не увидим атрибутов, которые добавляются к схеме в интерфейсе пользователя этой утилиты, управляемом мышью. Все можно сконфигурировать с помощью ADSI Edit, мы можем также просмотреть схему и конфигурацию. Эта утилита, однако, не так проста в использовании, и очень легко ввести неправильные данные:

    Открывая окно Properties (Свойства) объекта, можно изменить любой атрибут объекта в активном каталоге. Мы видим обязательные, дополнительные атрибуты, типы и значения атрибутов:

    ADSI Viewer

    Установим также браузер активного каталога как часть SDK платформы Microsoft. SDK платформы Microsoft не является частью дистрибутива Visual Studio.NET. Вы получаете компакт-диск с подпиской на MSDN или загружаете его с web-сайта MSDN. После установки SDK платформы можно запустить утилиту с помощью пункта меню Start|Programs|Microsoft Platform SDK|Tools|ADSI Viewer.

    ADSI Viewer имеет два режима. С помощью File|New можно запустить запрос или использовать Object Viewer для вывода и изменения атрибутов объектов. После запуска Object Viewer можно определить путь доступа LDAP, а также имя пользователя и пароль, чтобы открыть объект. Вскоре, когда мы начнем делать это программным путем, вы увидите, какую форму может иметь путь доступа LDAP. Здесь определяется

    LDAP://OU=Wrox Press, DC=eichkogelstrasse, DC=local
    , чтобы получить доступ к объекту организационной единицы:

    Если определяемый объект с путем доступа и именем пользователя и паролем является допустимым, мы получаем экран Object Viewer где можно видеть и изменять свойства объекта и объектов-потомков:

    Интерфейсы службы активного каталога (ADSI)

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

    System.DirectoryServices
    используют интерфейсы ADSI.

    На следующем рисунке можно видеть некоторых провайдеров ADSI (LDAP, WinNT, NDS), которые реализуют интерфейсы COM, такие как

    IAD
    и
    IUnknown
    . Сборка
    System.DirectoryServices
    использует провайдеров ADSI:

    Программирование активного каталога

    Чтобы разрабатывать программы для активного каталога, мы используем классы из пространства имен

    System.DirectoryServices
    . С помощью этих классов можно запрашивать объекты, просматривать и обновлять свойства, а также вести поиск объектов.

    В следующих сегментах кода простое консольное приложение C# демонстрирует применение классов в пространстве имен

    System.DirectoryServices
    .

    Классы в System.DirectoryServices

    Следующая таблица показывает основные классы в пространстве имен

    System.DirectoryServices
    :

    Класс Описание
    DirectoryEntry
    Этот класс является основным классом в пространстве имен
    System.DirectoryServices
    . Объект этого класса представляет объект в хранилище активного каталога. Мы используем этот класс для связывания с объектом, просмотра и обновления свойств. Свойства объекта представлены в
    PropertyCollection
    . Каждый элемент в
    PropertyCollection
    имеет в свою очередь
    PropertyValueCollection
    .
    DirectoryEntries
    DirectoryEntries
    является коллекцией объектов
    DirectoryEntry
    . Свойство
    Children
    объекта
    DirectoryEntry
    возвращает список объектов в коллекцию
    DirectoryEntries
    .
    DirectorySearcher
    Этот класс является основным классом, используемым для поиска объектов со специфическими атрибутами. Чтобы определить поиск, можно использовать класс
    SortOption
    и перечисления
    SearchScope
    ,
    SortDirection
    и
    ReferalChasingOption
    . Результаты поиска находятся в классе
    SearchResult
    или
    SearchResultCollection
    . Мы также получаем объекты
    ResultPropertyCollection
    и
    ResultPropertyValueCollection
    .

    Связывание

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

    LDAP://dc01.globalknowledge.net/OU=Marketing, DC=GlobalKnowledge, DC=Com

    Во время процесса связывания можно определить следующие позиции:

    □ Протокол, определяющий используемого провайдера.

    □ Имя сервера контроллера домена.

    □ Номер порта серверного процесса.

    □ Известное имя объекта для идентификации объекта, к которому требуется доступ.

    □ Имя пользователя и пароль, если требуется другой пользователь для доступа к активному каталогу.

    □ Можно также определить тип аутентификации, если требуется шифрование.

    Протокол

    Первая часть пути связывания определяет провайдера ADSI. Провайдер реализован как сервер COM; для идентификации можно найти идентификатор программы прямо в ключе

    HKEY_CLASSES_ROOT
    . Провайдеры, которых получают вместе с Windows 2000, перечислены в следующей таблице:

    Протокол Описание
    LDAP Сервер LDAP, такой как каталог Exchange и сервер активного каталога Windows 2000.
    GC GC применяется для доступа к глобальному каталогу в активном каталоге. Он может использоваться для быстрых запросов.
    IIS С помощью провайдера ADSI для IIS можно создавать новые web-сайты в каталоге IIS.
    WinNT Чтобы получить доступ к базе данных пользователей старых доменов Windows NT 4, можно использовать провайдера ADSI для WinNT, Факт, что пользователи NT 4 имеют только несколько атрибутов, остается неизменным.
    NDS Этот идентификатор программы используется для коммуникации со службами каталогов Novell.
    NWCOMPAT С помощью NWCOMPAT можно получить доступ к старым каталогам Novell: Novell Netware 3.x.

    Имя сервера

    Имя сервера следует за протоколом в строке соединения. Имя сервера является не обязательным, если вы зарегистрировались в домене активного каталога. Без имени сервера происходит связывание без сервера; это означает, что Windows 2000 пытается получить "лучший" контроллер домена в домене, который ассоциирован с пользователем, выполняющим связывание. Если внутри сайта нет сервера, будет использоваться первый найденный контроллер домена.

    Связывание без сервера может выглядеть следующим образом:

    LDAP://OU=Sales, DC=GlobalKnowladge, DC=Com
    .

    Номер порта

    После имени сервера можно определить номер порта серверного процесса, используя синтаксис

    :xxx
    . По умолчанию для сервера LDAP используется номер порта 389:
    LDAP://dc01.globalknowledge.net:389
    . Сервер Exchange использует тот же самый номер порта, что и сервер LDAP. Если сервер Exchange установлен на той же системе, например, как контроллер домена активного каталога, то можно сконфигурировать другой порт.

    Известное имя

    Четвертая часть, которую мы должны определить в пути доступа,— это известное имя (DN — Distinguished Name). Известное имя является гарантированным уникальным именем, идентифицирующим объект, к которому требуется доступ. В активном каталоге для определения имени объекта можно использовать синтаксис LDAP, который основывается на X.500.

    Известное имя

    CN=Christian Nagel, OU=Trainer, DC=GlobalKnowledge, DC=com

    определяет общее имя (

    CN
    ) Christian Nagel в организационной единице (
    OU
    ) Trainer в компоненте домена (
    DC
    ) GlobalKnowledge домена GlobalKnowledge.com. Часть, которая определена самой правой является корневым объектом домена. Имя должно следовать иерархии дерева объектов.

    Спецификацию LDAP для строкового представления известных имен можно найти в RFC 2253:

    www.ietf.org/rfc/rfc2253.txt
    .

    Относительное известное имя

    Относительное известное имя (RDN) используется для ссылки на объект внутри контейнерного объекта. Для RDN спецификации OU и DC не требуются, будет достаточно общего имени.

    CN=Christian Nagel
    является относительным известным именем внутри организационной единицы. Относительное известное имя может использоваться, если мы уже имеем ссылку на объект контейнера и хотим получить доступ к объектам-потомкам.

    Используемый по умолчанию именующий контекст

    Если известное имя не определено в пути доступа, процесс связывания будет выполняться в используемом по умолчанию именующем контексте. Можно считать используемый по умолчанию именующий контекст с помощью

    rootDSE
    . LDAP 3.0 определяет rootDSE как корень дерева каталогов на сервере каталога.

    LDAP://rootDSE
    или
    LDAP://servername/rootDSE

    Перечисляя все свойства

    rootDSE
    , можно получить информацию о
    defaultNamingContext
    , который будет использоваться, когда не определено никакое имя.
    schemaNamingContext
    и
    configurationNamingContext
    определяют требуемые имена, которые будут использоваться для доступа к схеме и конфигурации в хранилище активного каталога.

    Следующий код используется для получения всех свойств

    rootDSE
    . Речь идет о связывании с помощью класса
    DirectoryEntry
    :

    using (DirectoryEntry de = new DirectoryEntry()) {

     de.Path = "LDAP://celtlcrain/rootDSE";

     de.Username = @"sentinel\chris";

     de.Password = "mausemaus3";

     PropertyCollection props = de.Properties;

     foreach (string prop in props.PropertyNames) {

      PropertyValueCollection values = props[prop];

      foreach (string val in values) {

       Console.Write(prop + ": ");

       Console.WriteLine(val);.

      }

     }

    }

    Помимо других свойств результат вывода этой программы показывает

    defaultNamingContext DC=eichkogelstrasse, DC=local
    , контекст, который можно использовать для доступа к схеме:
    CN=Schema, CN=Configuration, DC=eichkogelstrasse, DC=local
    и именующий контекст конфигурации:
    CN=Configuration, DC=eichkogelstrasse, DC=local
    :

    Идентификатор объекта

    Каждый объект имеет уникальный идентификатор — GUID. GUID является уникальным 128-битовым числом. Мы можем с соединиться с объектом, используя GUID. Таким образом, мы всегда получаем тот же самый объект, даже если объект был перемещен в другой контейнер. GUID генерируется при создании объекта и всегда остается тем же самым.

    Можно получить строковое представление GUID с помощью

    DirectoryEntry.NativeGuid
    . Затем это строковое представление можно использовать для соединения с объектом. Даже если объект перемещается в другой контейнер, мы всегда получаем тот же объект.

    Следующий пример показывает имя пути доступа для связывания без сервера со специфическим объектом, представленным GUID:

    LDAP://<GUID=14abbd652aae1a47abc60782dcfc78ea>

    Имена объектов в доменах Windows NT

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

    ObjectName
    ,
    ClassName
    . Действительные строки связывания для домена Windows NT имеют следующий вид:

    WinNT:

    WinNT://DomainName

    WinNT://DomairName/UserName, user

    WinNT://DomainName/dc01/MyGroup, group

    Имя пользователя

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

    Низкоуровневая регистрация

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

    domain\username

    Известное имя

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

    CN=Administrator, CN=Users, DC=eichkogelstrasse, DC=local

    Имя пользователя принципала (UPN)

    UPN объекта определяется с помощью атрибута

    userPrincipalName
    . Системный администратор определяет его по информации регистрации на вкладке
    Account
    свойств
    User
    с помощью утилиты Active Directory Users and Computers. UPN не является адресом e-mail пользователя

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

    Nagel@eichkogestrasse.local

    Аутентификация

    Для безопасной зашифрованной аутентификации можно также определить тип аутентификации. Аутентификация может задаваться с помощью свойства

    AuthenticationType
    класса
    DirectoryEntry
    . При этом присваиваемое значение является одним из перечислений
    AuthenticationTypes
    .

    Связывание с помощью класса DirectoryEntry

    Класс

    System.DirectoryServices.DirectoryEntry
    используется для определения всех связываемых данных. Можно использовать конструктор по умолчанию и определить данные связывания со свойствами
    Path
    ,
    Username
    ,
    Password
    и
    AuthenticationType
    или передать всю информацию в конструктор:

    using (DirectoryEntry de = new DirectoryEntry()) {

     de.Path = "LDAP://celticrain/DC=eichkogelstrasse, DC=local";

     de.Username = "nagel@eichkogelstrasse.local";

     de.Password = "someSecret";

     // использовать полномочия текущего пользователя

     DirectoryEntry de2 = new DirectoryEntry("LDAP://DC=eichkogelstrasse, DC=local");

    Даже если создание объекта

    DirectoryEntry
    пройдет успешно, это не означает, что и связывание было успешным. Связывание произойдет, когда в первый раз будет прочитано свойство во избежание ненужного сетевого трафика. Существует ли объект или правильны ли полномочия определенного пользователя, можно будет увидеть при первом доступе к объекту.

    Получение записей каталога

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

    Свойства объектов пользователей

    Класс

    DirectoryEntry
    имеет некие свойства
    Name
    ,
    Guid
    и
    SchemaClassName
    для получения информации об объекте. Первый раз при доступе к свойству объекта
    DirectoryEntry
    происходит связывание и заполняется кэш. Когда мы обращаемся к другому свойству, мы считываем его из кэша, и коммуникации с сервером не требуется для данных из того же объекта.

    В следующем примере мы обращаемся к объекту пользователя с общим именем Christian Nagel в организационной единице Wrox Press.

    DirectoryEntry de = new DirectoryEntry();

    de.Path = "LDAP://celticrain/CN=Christian Nagel, " +

     "OU=Wrox Press, DC=eichkogelstrasse, DC=local";

    Console.WriteLine("Name: "+ de.Name);

    Console.WriteLine("GUID: " + de.Guid);

    Console.WriteLine("Type: " + de.SchemaClassName);

    Console.WriteLine();

    Объект активного каталога содержит значительно больше информации. Доступность информации зависит от типа объекта. Чтобы получить всю информацию об объекте, свойство

    Properties
    возвращает
    PropertyCollection
    . Каждое свойство само является коллекцией, так как одно свойство может иметь несколько значений, например, объект пользователя может иметь несколько телефонных номеров. Мы перебираем значения с помощью внутреннего цикла
    foreach
    . Коллекция, которая возвращается из
    properties[name]
    является массивом объектов. Значения атрибутов могут быть строками, числами или другими типами данных. Мы используем метод
    ToString()
    для вывода значений.

    Console.WriteLine("Attributes: ");

    PropertyCollection properties = de.Properties;

    foreach (string name in properties.PropertyNames) {

     foreach (object о in properties[name]) {

      Console.WriteLine(name + ": " + o.ToString());

     }

    }

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

    System._ComObject
    . Чтобы получить значения этих атрибутов, необходимо непосредственно использовать интерфейсы ADSI COM, которые также берутся из классов в пространстве имен
    System.DirectoryServices
    .

    В главе 19 можно прочитать, как работать с объектам и и интерфейсами COM.

    Для получения дополнительной информации об ADSI можно прочитать книгу Simon Robinson, Professional ADSI Programming, Wrox Press, ISBN 1-861002-26-2.

    Доступ к свойствам непосредственно по имени

    С помощью

    DirectoryEntry.Properties
    можно получить доступ ко всем свойствам. Если имя свойства известно, можно получить значение непосредственно:

    foreach (string homePage in de.Properties["wWWHomePage"])

     Console.WriteLine("Home page; " + homePage);

    Коллекции объектов

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

    Children
    класса
    DirectoryEntry
    . В другом направлении можно получить контейнер объекта с помощью свойства
    Parent
    .

    Объект пользователя не имеет потомков, поэтому воспользуемся теперь организационной единицей. Давайте получим все объекты пользователей из организационной единицы Wrox Press в домене eichkogelstrasse.local. Свойство

    Children
    возвращает коллекцию
    DirectoryEntries
    , которая содержит объекты
    DirectoryEntry
    . Мы просматриваем все объекты
    DirectoryEntry
    для вывода имен объектов-потомков:

    DirectoryEntry de = new DirectoryEntry();

    de.Path. = "LDAP://celticrain/OU=Wrox Press, " + "DC=eichkogelstrasse, DC=local";

    Console.WriteLine("Children of " + de.Name);

    foreach (DirectoryEntry obj in de.Children) {

     Console.WriteLine(obj.Name);

    }

    В данном примере мы видим все объекты в организационной единице: пользователей, контакты, принтеры, общие ресурсы и другие организационные единицы. Если нужно увидеть только некоторые типы объектов, можно использовать свойство

    SchemaFilter
    класса
    DirectoryEntries
    :

    DirectoryEntry de = new DirectoryEntry();

    de.Path = "LDAP://celticrain/OU=Wrox Press, " + "DC=eichkogelstrasse, DC-local";

    Console.WriteLine("Children of " + de.Name);

    de.Children.SchemaFilter.Add("user");

    foreach(DirectoryEntry obj in de.Children) {

     Console.WriteLine(obj.Name);

    }

    В результате мы видим в организационной единице только объекты пользователей:

    Кэш

    Чтобы уменьшить сетевой трафик, ADSI использует кэш для свойств объектов. Как было показано ранее, обращение к серверу не происходит при создании объекта

    DirectoryEntry
    , а происходит, когда впервые считывается значение из хранилища каталога. При считывании первого свойства все свойства записываются в кэш, поэтому повторное обращение к серверу не нужно, когда считывается следующее свойство. Этот кэш свойств может быть выключен при задании свойства
    DirectoryEntry.UsePropertyCache
    как
    false
    . Лучше этого не делать, так как это будет порождать множество ненужных обращений к серверу.

    Запись изменений в объекты также происходит только в кэше. Задание множества свойств не генерирует сетевого трафика. Метод

    DirectoryEntry.CommitChanges()
    требуется для очистки кэша и переноса всех измененных данных на сервер. Чтобы снова получить вновь записанные данные из хранилища каталога, можно для чтения свойств использовать метод
    DirectoryEntry.RefreshCache()
    . Задание свойства
    UsePropertyCache
    как
    false
    может быть очень полезно для отладки, чтобы увидеть, какое свойство было изменено неправильно.

    Обновление записей каталога

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

    PropertyValueCollection.Clear()
    . С помощью метода
    Add()
    к свойству могут добавляться новые значения.
    Remove()
    и
    RemoveAt()
    удаляют специфические значения из коллекции свойства:

    using (DirectoryEntry de = new DirectoryEntry!)) {

     de.Path =

      "LDAP://celticrain/CN=Christian Nagel, " +

      "OU=Wrox Press, DC=eichkogelstrasse, DC=local";

     if (de.Properties.Contains("mobile")) {

      de.Properties["mobile"][0] = "+43 (664) 3434343434";

     }

     de.CommitChanges();

    }

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

    PropertyValueCollection
    . С помощью индекса значение может только изменяться. Поэтому необходимо всегда проверять методом
    DirectoryEntry.Properties.Contains()
    , доступен ли атрибут:

    de.Properties["mobile"][0] = "+43 (664) 3434343434";

    He забудьте вызвать метод

    DirectoryEntry.CommitChanges()
    после создания или обновления новых объектов каталога. Иначе обновляется только кэш, а изменения не посылаются службе каталога.

    Создание новых объектов

    Новые объекты активного каталога, такие как пользователи, компьютеры, принтеры, контакты и другие программным путем можно создать с помощью класса

    DirectoryEntries
    .

    Чтобы добавить новые объекты в каталог, мы должны сначала соединиться с объектом-контейнером, подобным организационной единице, куда можно вставить новые объекты. Объекты, которые не могут содержать другие объекты, использовать нельзя. Здесь используется контейнерный объект с известным именем

    CN=Users, DC=eichkogelstrasse, DC=local
    :

    DirectoryEntry de = new DirectoryEntry();

    de.Path = "LDAP://celticrain/CN=Users, " +

     "DC=eichkogelstrasse, DC=local";

    Можно получить доступ к объекту

    DirectoryEntries
    с помощью свойства
    Children
    объекта
    DirectoryEntry
    :

    DirectoryEntries users = de.Children;

    Объект

    DirectoryEntries
    имеет методы для добавления, удаления, и поиска объектов в коллекции. Здесь создается новый объект пользователя. Для метода
    Add()
    нам нужно имя объекта и имя типа. Можно легко получить имена типов с помощью ADSI Edit.

    DirectoryEntry user = users.Add("John Doe", "user");

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

    Add()
    свойства
    Properties
    . Конечно, все свойства должны существовать в схеме для объекта пользователя. Если определенное свойство не существует, то возникнет исключение
    COMException
    "The specified directory service attribute or value doesn't exist" ("Указанный атрибут или значение службы каталога не существует"). Если имена атрибутов правильны, но сервер отказывает во входе в связи с незаконным паролем или пропущенным свойством, исключение
    COMException
    будет содержать сообщение "The server is unwilling to process the request" ("Сервер не желает обрабатывать запрос").

    user.Properties["company"].Add("Some Company");

    user.Properties["department"].Add("Sales");

    user.properties["employeeID"].Add("4711");

    user.Properties["samAccountName"].Add("John Doe");

    user.Properties["userPassword"].Add("someSecret");

    В данный момент не все данные записаны в активный каталог. Необходимо очистить кэш:

    user.CommitChanges();

    Поиск в активном каталоге

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

    Для поиска в активном каталоге платформа .NET имеет класс

    DirectorySearcher
    .

    Мы можем использовать поиск только с провайдером LDAP.

    DirectorySearcher
    не работает с провайдерами NDS или IIS.

    В конструкторе класса

    DirectorySearcher
    существуют четыре важные части поиска. Можно также использовать конструктор по умолчанию и определять параметры поиска с помощью свойств.

    SearchRoot

    Корень поиска (

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

    Filter

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

    Операторы отношений, такие как

    <=
    ,
    =
    ,
    >=
    , в выражениях допускаются. (
    objectClass = contact
    ) будет искать все объекты типа
    contact
    ; (
    lastName>=Nagel
    ) ищет все объекты, где свойство
    lastName
    равно или больше
    Nagel
    , что означает, что оно следует за ним в алфавитном порядке.

    Выражения могут комбинироваться с префиксными операциями

    &
    и
    |
    . (
    &(objectClass=user)(description=Auth*)
    ) ищет все объекты типа
    user
    , где свойство
    description
    начинается со строки
    Auth
    . Так как операторы
    &
    и
    |
    находятся в начале выражения, то с помощью одного префиксного оператора можно комбинировать более двух выражений.

    По умолчанию используется фильтр (

    objectClass=*
    ), поэтому все объекты допустимы. Синтаксис фильтра определен в RFC 2254, "Строковое представление фильтров поиска LDAP". Этот RFC можно найти по адресу
    www.ietf.org/rfc/rfc2254.txt
    .

    PropertiesToLoad

    С помощью

    PropertiesToLoad
    мы определяем коллекцию
    StringCollection
    всех интересующих нас свойств. Как вы уже видели, объекты могут иметь множество свойств. Большинство из них будут не важны для нашего запроса поиска. Мы определяем свойства, загружаемые в кэш. Свойствами по умолчанию, которые мы получаем, если ничего не определено, являются
    Path
    и
    Name
    для объекта.

    SearchScope

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

    □ 

    SearchScope.Base
    ищет атрибуты только в том объекте, где начинается поиск, поэтому мы получаем максимум один объект.

    □ Для

    SearchScope.OneLevel
    поиск продолжается в коллекции-потомке базового объекта. Сам базовый объект для поиска не используется.

    SearchScope.Subtree
    определяет, что поиск должен спускаться вниз по всему дереву.

    По умолчанию для

    SearchScope
    используется
    Subtree
    .

    Пределы поиска

    Такой поиск может охватывать несколько доменов. Чтобы ограничить поиск некоторым числом объектов или требуемым временем, необходимо определить несколько дополнительных свойств.

    Свойства DirectorySearcher Описание
    ClientTimeout
    Максимальное время, в течение которого клиент ожидает, что сервер вернет результат. Если сервер не отвечает, то никаких записей не возвращается.
    PageSize
    При постраничном поиске сервер возвращает число объектов, определенных с помощью
    PageSize
    , а не весь результат. Это сокращает и время клиента для получения первого ответа, и необходимую память. Сервер посылает клиенту cookie, которое отправляется назад на сервер с запросом следующего поиска, чтобы поиск можно было продолжить в точке, где он закончился.
    ServerPageTimeLimit
    Это значение определяет время для постраничного поиска, чтобы вернуть число объектов, которое определено значением
    PageSize
    . Если время истекает до достижения значения
    PageSize
    , найденные до этого момента объекты возвращаются клиенту. Значение по умолчанию равно -1, что означает бесконечность.
    ServerTimeLimit
    Определяет максимальное время, в течение которого сервер будет искать объекты. Когда это время истекает, все найденные до этого момента объекты возвращаются клиенту. По умолчанию используется 120 секунд, и нельзя задать время поиска больше этого значения.
    ReferalChasing
    Поиск может распространяться на несколько доменов. Если корень, который определен в
    SearchRoot
    , является родительским доменом или корень не был определен, поиск может распространиться на домены-потомки. С помощью этого свойства можно определить, что поиск должен продолжаться на других серверах.
    ReferalChasingOption.None
    означает, что поиск не продолжается на другие серверы. С помощью значения
    ReferalChasingOption.Subordinate
    можно определить, что поиск должен переходить на домены-потомки. Когда поиск начинается в DC=Wrox, DC=COM, сервер возвращает множество результатов и ссылку на DC=France, DC=Wrox, DC=COM. Клиент может продолжить поиск в поддомене.
    ReferalChasingOption.External
    означает, что сервер может направить клиента на независимый сервер, которого нет в поддомене. Это вариант поведения по умолчанию. Для
    ReferalChasingOption.All
    возвращаются ссылки на внешние домены и подчиненные домены.

    В рассматриваемом примере поиска мы хотим найти все объекты пользователей в организационной единице Wrox Press, где свойство

    description
    содержит значение
    Author
    .

    Сначала мы соединяемся с организационной единицей Wrox Press. Здесь начинается поиск. Создадим объект

    DirectorySearcher
    , где задан
    SearchRoot
    . Фильтр определяется как (
    &(objectClass=user)(description=Auth*)
    ) для того, чтобы мы нашли все объекты типа
    user
    , где свойство
    description
    начинается с последовательности
    Auth
    , за которой может следовать что-то еще. Область поиска должна быть поддеревом, чтобы поиск происходил в порождаемых организационных единицах для Wrox Press:

    DirectoryEntry de new DirectoryEntry();

    de.Path = "LDAP://OU=Wrox Press, " + "DC=eichkogelstrasse, DC=local";

    DirectorySearcher searcher = new DirectorySearcher();

    searcher.SearchRoot  = de;

    searcher.Filter = "(&(objectClass=user)(description=Auth*))";

    searcher.SearchScope = SearchScope.Subtree;

    В результате поиска мы хотим получить свойства

    name
    ,
    description
    ,
    givenName
    , и
    wWWHomePage
    .

    searcher.PropertiesToLoad.Add("name");

    searcher.PropertiesToLoad.Add("description");

    searcher. PropertiesToLoad.Add("givenName");

    searcher.PropertiesToLoad.Add("wWWHomePage");

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

    DirectorySearcher
    имеет свойство
    Sort
    , где можно задать
    SortOption
    . Первый аргумент конструктора
    SortOption
    определяет свойство, по которому будет проводиться сортировка, второй аргумент определяет направление сортировки. Перечисление
    SortDirection
    имеет значения
    Ascending
    и
    Descending
    .

    Чтобы начать поиск, можно использовать метод

    FindOne()
    для нахождения первого объекта или
    FindAll()
    , чтобы найти все объекты.
    FindOne()
    вернет простой
    SearchResult
    ,
    FindAll()
    вернет
    SearchResultCollection
    . Мы хотим получить всех авторов, поэтому используем
    FindAll()
    :

    searcher.Sort = new SortOption("givenName", SortDirection.Ascending);

    SearchResultCollection Results = searcher.FindAll();

    С помощью цикла

    foreach
    мы получаем доступ ко всем
    SearchResult
    в
    SearchResultCollection
    .
    SearchResult
    представляет один объект в кэше поиска. Свойство
    Properties
    возвращает
    ResultPropertyCollection
    , где мы получаем доступ ко всем свойствам и значениям по имени свойства и по индексу.

    SearchResultCollection results = Searcher.FindAll();

     foreach (SearchResult result in results) {

      ResutPropertyCollection props = result.Properties;

      foreach (string propName in props.PropertyNames) {

       Console.Write(propName + ": ");

       Console.WriteLine(props[propName][0]);

      }

      Console.WriteLine();

     }

    }

    Если необходимо получить весь объект после поиска, то это также возможна.

    SearchResult
    имеет метод
    GetDirectoryEntry()
    , который возвращает соответствующую запись
    DirectoryEntry
    найденного объекта.

    Результирующий вывод показывает начале списка всех авторов книги Professional C# с выбранными свойствами

    Поиск объектов пользователей

    Последнее приложение, которое будет создано в этой главе, это приложение Windows Forms. С его помощью можно найти все объекты пользователей домена с динамически определяемой строкой фильтра. Можно также задать свойства объектов пользователей, которые должны выводиться.

    Интерфейс пользователя

    Интерфейс пользователя выводит нумерованные шаги, помогая использовать приложение.

    1. На первом шаге можно ввести имя пользователя, пароль и контроллер домена. Вся эта информация является необязательной. Если контроллер домена не вводится, то соединение работает со связыванием без сервера. Если отсутствует имя пользователя, то используется контекст безопасности текущего пользователя.

    2. С помощью кнопки все имена свойств объекта

    User
    могут загружаться динамически в окно списка
    ListBoxProperties
    .

    3. После загрузки имен свойств, можно выбрать свойства, которые должны выводиться. Режим

    SelectionMode
    окна списка задач как
    MultiSimple
    .

    4. Можно ввести фильтр для ограничения поиска. Значение по умолчанию, которое задается в этом диалоговом окне, ищет все объекты пользователей: (

    objectClass=user
    ).

    5. Теперь можно начать поиск.

    Получение именующего контекста схемы

    Это приложение имеет только два метода обработки событий: первый метод — обработчик для кнопки загрузки свойств и второй — для запуска поиска в домене. В первой части мы динамически считываем свойства класса

    User
    из схемы для вывода его в интерфейсе пользователя.

    В методе-обработчике

    buttonLoadProperties_Click()
    с помощью метода
    SetLogonInformation()
    имя пользователя, пароль и имя хоста считываются во время диалога и сохраняются в членах класса. Затем метод
    SetNamingContext()
    задает имя LDAP схемы и имя LDAP используемого по умолчанию контекста. Имя LDAP этой схемы используется в вызове
    SetUserProperties()
    для задания свойств в окне списка:

    private void buttonLoadProperties_Click(object sender, System.EventArgs e) {

     try {

      SetLogonInformation();

      SetNamingContext();

      SetUserProperties(schemaNamingContext);

     } catch (Exception ex) {

    MessageBox.Show("Cheek your inputs! " + ex.Message);

     }

    }


    protected void SetLogonInformation() {

     username =

      (textBoxUsername.Text == "" ? null :

      textBoxUsername.Text);

     password =

      (textBoxPassword.Text == "" ? null :

      textBoxPassword.Text);

     hostname = textBoxHostname.Text;

     if (hostname ! = "") hostname += "/";

    }

    Во вспомогательном методе

    SetNamingContext()
    мы используем корень дерева каталога для получения свойств сервера. Мы заинтересованы в значениях двух свойств:
    SchemaNamingContext
    .

    protected string SetNamingContext() {

     using (DirectoryEntry de = new DirectoryEntry()) {

      string path = "LDAP://" + hostname + "/rootDSE";

     de.Username = username;

     de.Password = password;

     de.Path = path;

     schemaNamingContext =

      de.Properties["schemaNamingContext"][0].ToString();

     defaultNamingContext =

      de.Properties["defaultNamingContext"][0].ToString();

     }

    }

    Получение имен свойств класса пользователя

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

    User
    :
    Organizational-Person
    ,
    Person
    и
    Top
    . В этой программе имена базовых классов жестко закодированы. Можно было бы прочитать базовый класс динамически с помощью атрибута
    subClassOf
    . Метод
    GetSchemaProperties()
    возвращает строковый массив со всеми именами свойств определенного типа объектов. Все имена свойств собраны в объекте
    properties
    типа
    StringCollection
    :

    protected void SetUserProperties(string schemaNamingContext) {

     StringCollection properties = new StringCollection();

     string[] data = GetSchemaProperties(schemaNamingContext, "User");

     properties.AddRange(GetSchemaProperties(schemaNamingContext, "Organizational-Person"));

     properties.AddRange(GetSchemaProperties(schemaNamingContext, "Person"));

     properties.AddRange(GetSchemaProperties(schemaNamingContext, "Top"));

     listBoxProperties.Items.Clear();

     foreach (string s in properties) {

      listBoxProperties.Items.Add(s);

     }

    }

    В методе

    GetSchemaProperties()
    мы снова обращаемся к активному каталогу. В этот раз вместо
    rootDSE
    используется имя LDAP в схеме, которое мы обнаружили ранее. Свойство
    systemMayContain
    содержит коллекцию всех атрибутов, которые допустимы в классе
    objectType
    :

    protected string[] GetSchemaProperties(string schemaNamingContext, string objectType) {

     string [] data;

     using (DirectoryEntry de = new DirectoryEntry()) {

      de.Username = username;

      de.Password = password;

      de.Path = "LDAP://" + hostname + "/CN=" + objectType + "," + schemaNamingContext;

      DS.PropertyCollection properties = de.Properties;

      DS.PropertyValueCollection values = properties["systemMayContain"];

      data = new String[values.Count];

      values.CopyTo(data, 0);

     }

     return data;

    }

    Одно интересное замечание к этому коду: в приложении Windows Forms класс

    PropertyCollection
    пространства имен
    System.DirectoryServices
    имеет конфликт имен с
    System.Data.PropertyCollection
    . Поскольку писать такие длинные имена как
    System.DirectoryServices.PropertyCollection
    не всегда хочется, то с целью разрешения конфликта имя пространства имен можно сократить с помощью

    namespace DS = System.DirectoryServices;

    Именно отсюда появляется

    DS.PropertyCollection
    .

    Шаг 2 приложения завершен. Окно списка (

    listbox
    ) содержит все имена свойств объектов
    User
    .

    Поиск объектов User

    Обработчик для кнопки поиска вызывает вспомогательный метод

    FillResult()
    :

    private void buttonSearch_Click(object render, System.EventArgs e) {

     try {

      FillResult();

     } catch (Exception ex) {

      MessageBox.Show("Check your input: " + ex.Message)

     }

    }

    В методе

    FillResult()
    выполняется обычный поиск в полном домене активного каталога, как мы видели раньше.
    SearchScope
    задается как
    Subtree
    ,
    Filter
    для строки мы получаем из
    TextBox
    , а свойства, которые должны быть загружены в кэш, задаются значениями, которые пользователь выбирает в окне списка

    protected void FillResult() {

     using (DirectoryEntry root = new DirectoryEntry()) {

      root.Username = username;

      root.Password = password;

      root.Path = "LDAP://" + hostname + defaultNamingContext;

      using (DirectorySearcher searcher = new DirectorySearcher()) {

       seacher.SearchRoot = root;

       searcher.SearchScope = SearchScope.Subtree;

       searcher.Filter = textboxfilter.Text;

       searcher.PropertiesToLoad.AddRange(GetProperties());

       SearchResultCollection results = searcher.FindbAll();

       StringBuilder summary = new StringBuilder();

       foreach (SearchResult result in results) {

        foreach (string propName in result.Properties.PropertyNames) {

         foreach (string s in result.Properties[propName]) {

          summary.Append(" " + propName + ": " + s + "\r\n");

         }

        }

        summary.Append("\r\n");

       }

       textBoxResults.Text = summary.ToString();

      }

     }

    }

    Запустив приложение, мы получим список всех объектов, которые прошли через фильтр:

    Заключение

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

    С помощью классов из пространства имен

    System.DirectoryServices
    мы получаем простые способы доступа к активному каталогу для провайдеров ADSI. Класс
    DirectoryEntry
    делает возможным чтение и запись объектов непосредственно в хранилище данных.

    С помощью класса

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







     

    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх