При интеграции 1С с Active Directory (AD) программисты часто сталкиваются с ситуацией, когда запрос к каталогу возвращает пустой результат или ошибку: "Произошла исключительная ситуация (ADODB.Recordset): BOF или EOF имеет значение True". Особенно часто это происходит при попытке получить специфические атрибуты, такие как employeeID. В рамках данной статьи мы разберем, почему возникают эти ошибки, и рассмотрим наиболее надежные способы подключения к AD на стороне сервера.
Проанализируем ситуацию, описанную в исходном сообщении. Использование строки подключения вида GC://ИмяДомена означает обращение к Global Catalog (Глобальному каталогу). Глобальный каталог предназначен для быстрого поиска объектов по всему лесу доменов, однако он имеет существенное ограничение.
Рассмотрим подробнее: по умолчанию Глобальный каталог содержит только Partial Attribute Set (частичный набор атрибутов). Это базовый набор полей (имя, логин, почта), необходимых для идентификации. Атрибут employeeID в этот стандартный набор не входит. Если ваш системный администратор вручную не включил репликацию этого атрибута в GC, запрос через этот протокол всегда будет возвращать EOF (пустой результат), даже если в самом AD данные заполнены.
Решение: Для получения полных данных об объекте необходимо использовать протокол LDAP://. Он позволяет обращаться к полной схеме данных конкретного домена.
Если вы привыкли работать с объектами ADODB.Connection и ADODB.Command, давайте модифицируем ваш код так, чтобы он стал более стабильным. Разберем по шагам правильную последовательность действий.
Во-первых, важно явно указывать Page Size. По умолчанию AD возвращает не более 1000 записей. Если в вашем справочнике больше сотрудников, без постраничной выборки вы получите ошибку или неполные данные.
// Создаем соединение
Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.Provider = "ADsDSOObject";
Соединение.Open("Active Directory Provider");
// Настраиваем команду
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = Соединение;
// Указываем путь к корню домена (замените на свои параметры)
ПутьLDAP = "LDAP://" + СокрЛП(НашДомен);
// Текст запроса. Обратите внимание на использование LDAP вместо GC
Команда.CommandText = "<" + ПутьLDAP + ">;(&(objectCategory=person)(objectClass=user)(sAMAccountName=" + Логин + "));employeeID,sAMAccountName;subtree";
// Обязательно устанавливаем размер страницы для больших выборок
Команда.Properties("Page Size").Value = 100;
Команда.Properties("Timeout").Value = 30;
Команда.Properties("Cache Results").Value = Ложь;
Попытка
Выборка = Команда.Execute();
Исключение
Сообщить("Ошибка при выполнении запроса к AD: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Если Не Выборка.EOF Тогда
ЗначениеID = Выборка.Fields("employeeID").Value;
КонецЕсли;
Выясним причину популярности метода через ПолучитьCOMОбъект. Этот способ считается более "легким", так как не требует настройки провайдеров ADO. Он отлично подходит для точечного получения данных по конкретному контейнеру (OU).
Посмотрим на пример реализации:
// Указываем путь к конкретному контейнеру или корню домена
ПутьКОбъекту = "LDAP://CN=Users,DC=company,DC=local";
Попытка
ДоменОбъект = ПолучитьCOMОбъект(ПутьКОбъекту);
Исключение
Сообщить("Не удалось подключиться к домену. Проверьте DNS и права доступа.");
Возврат;
КонецПопытки;
// Перебор всех объектов в контейнере
Для Каждого ПользовательAD Из ДоменОбъект Цикл
// Проверяем, что это объект пользователя
Если ПользовательAD.Class = "user" Тогда
// Читаем атрибут напрямую
Попытка
СотрудникID = ПользовательAD.employeeID;
Если ЗначениеЗаполнено(СотрудникID) Тогда
Сообщить("Пользователь: " + ПользовательAD.sAMAccountName + ", ID: " + СотрудникID);
КонецЕсли;
Исключение
// Атрибут может быть не задан в схеме или отсутствовать у объекта
КонецПопытки;
КонецЕсли;
КонецЦикла;
Рассмотрим наиболее современный и стабильный подход для работы на стороне сервера 1С. Использование System.DirectoryServices через COM-объект позволяет избежать многих проблем с типами данных и кодировками, которые возникают в старом ADO.
Этот метод предпочтителен, так как он нативно поддерживает современные протоколы аутентификации Kerberos и стабильнее работает в контексте серверных вызовов 1С.
// Используем DirectorySearcher через оболочку .NET
// В 1С это можно реализовать через типы COM или внешние компоненты
// Ниже представлен алгоритм логики поиска
Searcher = Новый COMОбъект("System.DirectoryServices.DirectorySearcher");
Searcher.Filter = "(&(objectClass=user)(sAMAccountName=" + Логин + "))";
Searcher.PropertiesToLoad.Add("employeeID");
Результат = Searcher.FindOne();
Если Результат <> Неопределено Тогда
Значение = Результат.Properties.Item("employeeID").Item(0);
КонецЕсли;
Если код написан верно, протокол заменен на LDAP, но ошибка EOF сохраняется, проанализируем настройки безопасности самого Active Directory. Атрибут employeeID часто помечается как Confidential или требует специальных прав на чтение (Personal Identifiable Information).
Важные моменты:
USR1CV8). Убедитесь, что эта учетная запись имеет права "Read" на атрибут employeeID для всех объектов пользователей.dc1.mydomain.local, чтобы исключить проблемы с разрешением имен.Подводя итог нашему разбору, можно выделить следующую последовательность действий для исправления ошибки:
GC:// на LDAP:// в строке подключения, чтобы получить доступ ко всей схеме атрибутов.Page Size, иначе при большом количестве данных Recordset может вести себя непредсказуемо.ADODB на использование объектов System.DirectoryServices.Используя предложенные методы, вы сможете построить стабильную систему синхронизации пользователей между 1С и Active Directory, минимизировав количество ошибок при обращении к расширенным атрибутам данных.