В этой статье мы подробно рассмотрим, как реализовать автоматическую синхронизацию пользователей 1С с данными из Active Directory (AD). Эта задача часто возникает в крупных компаниях, где необходимо централизованно управлять учетными записями, избегая ручного дублирования создания пользователей в разных системах. Мы проанализируем основные подходы, разберем необходимые шаги и предоставим примеры кода, чтобы вы могли настроить такую интеграцию на своей платформе 1С:Предприятие 8.3 с использованием управляемых форм. Нашей целью будет создание регламентного задания, которое регулярно проверяет AD на наличие новых пользователей и автоматически создает их в информационной базе 1С.
Процесс синхронизации пользователей между Active Directory и 1С является важной частью системного администрирования в корпоративной среде. Он позволяет не только сократить ручной труд и минимизировать ошибки, но и значительно повысить уровень безопасности, обеспечивая единую точку управления доступом. Это особенно актуально, если в вашей организации внедряется комплексная концепция защищенной IT инфраструктуры. Пользователи смогут использовать свои доменные учетные данные для входа в систему 1С, что упрощает их работу и исключает необходимость запоминания нескольких паролей. Кстати, для случаев работы через тонкий клиент вне офиса может быть полезен лаунчер для 1C с проверкой доменной авторизации. Мы сосредоточимся на решении, которое будет работать на серверной стороне 1С, что является наиболее надежным и производительным подходом для автоматической синхронизации.
Для того чтобы 1С могла "видеть" и обрабатывать данные из Active Directory, нам потребуется использовать стандартные механизмы работы с внешними COM-объектами. Active Directory предоставляет интерфейс ADSI (Active Directory Service Interfaces), доступ к которому из 1С осуществляется через
COMОбъект
Процесс синхронизации будет состоять из нескольких ключевых этапов, которые мы будем последовательно реализовывать:
Важно отметить, что для успешного взаимодействия 1С с AD, учетная запись, под которой запускается сервер 1С (или клиент, если регламентное задание выполняется на клиенте в файловой базе), должна иметь достаточные права на чтение данных из Active Directory. Обычно это означает, что сервисная учетная запись сервера 1С должна быть членом домена и иметь права на чтение объектов из AD. Без соответствующих прав попытки подключения к AD будут завершаться ошибками.
Для начала нам необходимо установить соединение с Active Directory. Мы будем использовать
COMОбъект("ADsDSOObject")
ADsDSOObject
sAMAccountName
Нам потребуется указать строку подключения к LDAP, которая обычно выглядит как "LDAP://ваш_домен.локал". Это базовая строка, указывающая на доменный контроллер, с которым мы хотим установить соединение. Мы будем использовать объект
ADODB.Command
ADODB.Recordset
Execute()
// В общем модуле с флагами 'Сервер' и 'Внешнее соединение'
Функция ПолучитьПользователейИзActiveDirectory(ДоменLDAP)
СписокПользователейAD = Новый Массив;
Попытка
// Создаем объект для работы с ADSI.
// ADsDSOObject является поставщиком данных для доступа к каталогам через ADSI.
ADsDSO = Новый COMОбъект("ADsDSOObject");
// Открываем соединение с Active Directory, используя указанный домен LDAP.
// Пустые строки для логина/пароля означают использование текущих учетных данных
// процесса, в котором работает 1С-сервер (или клиент).
СоединениеAD = ADsDSO.OpenDSObject("LDAP://" + ДоменLDAP, "", "", 0);
// Создаем объект ADODB.Command для выполнения запросов к AD.
// ADODB.Command позволяет нам строить и выполнять SQL-подобные запросы.
ОбъектЗапроса = Новый COMОбъект("ADODB.Command");
ОбъектЗапроса.ActiveConnection = СоединениеAD; // Привязываем запрос к нашему соединению
// Строка LDAP-запроса. Это мощный инструмент для фильтрации и выбора данных.
// Она состоит из четырех частей, разделенных точкой с запятой:
// 1. Корень поиска: - указывает, где начинать поиск.
// 2. LDAP-фильтр: (&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(sAMAccountName=*))
// - (objectClass=user): ищем объекты, которые являются пользователями.
// - (!(userAccountControl:1.2.840.113556.1.4.803:=2)): исключаем пользователей с флагом UF_ACCOUNT_DISABLED (активные пользователи).
// Использование ":1.2.840.113556.1.4.803:=" позволяет фильтровать по битовым флагам.
// - (sAMAccountName=*): убеждаемся, что у пользователя есть sAMAccountName.
// 3. Список атрибутов: sAMAccountName,mail,cn,sn,givenName,displayName
// - sAMAccountName: Имя учетной записи в домене (Login).
// - mail: Адрес электронной почты.
// - cn: Каноническое имя (Common Name), часто используется как полное имя.
// - sn: Фамилия (Surname).
// - givenName: Имя (First Name).
// - displayName: Отображаемое имя, обычно 'Фамилия Имя Отчество'.
// 4. Область поиска: subtree - означает поиск во всем поддереве каталога, начиная с корня.
СтрокаЗапроса = ";(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(sAMAccountName=*));sAMAccountName,mail,cn,sn,givenName,displayName;subtree";
ОбъектЗапроса.CommandText = СтрокаЗапроса; // Присваиваем сформированную строку запроса
// Выполняем запрос и получаем набор записей (ADODB.Recordset).
РезультатЗапроса = ОбъектЗапроса.Execute();
// Перебираем полученные записи из Active Directory.
Пока Не РезультатЗапроса.EOF Цикл
СтруктураПользователя = Новый Структура;
// Получаем значения атрибутов для каждого пользователя.
// Проверяем, что значение атрибута не пустое перед добавлением в структуру.
СтруктураПользователя.Вставить("SAMAccountName", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("sAMAccountName").Value), РезультатЗапроса.Fields("sAMAccountName").Value, ""));
СтруктураПользователя.Вставить("Почта", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("mail").Value), РезультатЗапроса.Fields("mail").Value, ""));
СтруктураПользователя.Вставить("ПолноеИмя", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("cn").Value), РезультатЗапроса.Fields("cn").Value, ""));
СтруктураПользователя.Вставить("ОтображаемоеИмя", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("displayName").Value), РезультатЗапроса.Fields("displayName").Value, ""));
СтруктураПользователя.Вставить("Фамилия", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("sn").Value), РезультатЗапроса.Fields("sn").Value, ""));
СтруктураПользователя.Вставить("Имя", ?(ЗначениеЗаполнено(РезультатЗапроса.Fields("givenName").Value), РезультатЗапроса.Fields("givenName").Value, ""));
СписокПользователейAD.Добавить(СтруктураПользователя);
РезультатЗапроса.MoveNext(); // Переходим к следующей записи
КонецЦикла;
Возврат СписокПользователейAD;
Исключение
// В случае ошибки записываем ее в журнал регистрации для дальнейшего анализа.
ЗаписьЖурналаРегистрации("Ошибка при получении пользователей из Active Directory: " + ОписаниеОшибки(), УровеньВажностиЖурналаСобытий.Ошибка);
Возврат Неопределено;
КонецПопытки;
КонецФункции
Этот код возвращает массив структур, где каждая структура представляет собой данные одного пользователя из AD. Мы используем оператор
?()
После того как мы получили список пользователей из Active Directory, нам необходимо проверить их наличие в 1С и при необходимости создать новых. Для работы с пользователями информационной базы 1С мы используем менеджер объекта
ПользователиИнформационнойБазы
Давайте создадим функцию, которая будет принимать данные пользователя из AD и создавать или обновлять соответствующего пользователя в 1С. В этом процессе мы также настроим аутентификацию, что является критически важным шагом для обеспечения корректного входа пользователей в систему.
// В общем модуле с флагами 'Сервер' и 'Внешнее соединение'
Функция СоздатьИлиОбновитьПользователя1С(ДанныеПользователяAD, ДоменAD)
// Ищем пользователя 1С по имени пользователя в домене (sAMAccountName).
// Мы предполагаем, что имя пользователя в 1С будет совпадать с sAMAccountName из AD.
ИмяПользователя1С = ДанныеПользователяAD.SAMAccountName;
Пользователь1С = ПользователиИнформационнойБазы.НайтиПоИмени(ИмяПользователя1С);
Если Пользователь1С = Неопределено Тогда
// Пользователь не найден, создаем нового.
НовыйПользователь = ПользователиИнформационнойБазы.Создать();
НовыйПользователь.Имя = ИмяПользователя1С;
НовыйПользователь.ПолноеИмя = ДанныеПользователяAD.ОтображаемоеИмя; // Используем ОтображаемоеИмя из AD как ПолноеИмя в 1С
НовыйПользователь.Комментарий = "Автоматически создан из Active Directory";
НовыйПользователь.Недействителен = Ложь; // По умолчанию делаем пользователя активным
// Настройка аутентификации. Это ключевой момент для работы с AD.
// Вариант 1: Использование аутентификации ОС (предпочтительный для AD-интеграции).
НовыйПользователь.АутентификацияСтандартная = Ложь; // Отключаем стандартную аутентификацию 1С.
НовыйПользователь.АутентификацияОС = Истина; // Включаем аутентификацию операционной системы.
НовыйПользователь.ПользовательОС = ДоменAD + "\" + ИмяПользователя1С; // Указываем полное имя пользователя ОС, включая домен.
// Это связывает пользователя 1С с конкретной учетной записью Windows/AD.
// Вариант 2 (если нужна только стандартная аутентификация 1С и AD не используется для входа):
// НовыйПользователь.АутентификацияСтандартная = Истина;
// НовыйПользователь.АутентификацияОС = Ложь;
// НовыйПользователь.Пароль = СгенерироватьСлучайныйПароль(); // Необходимо реализовать функцию генерации пароля.
// НовыйПользователь.ПотребуетсяСменаПароля = Истина; // Рекомендуется, чтобы пользователь при первом входе сменил пароль.
Попытка
НовыйПользователь.Записать(); // Записываем нового пользователя в информационную базу.
ЗаписьЖурналаРегистрации("Создан новый пользователь 1С из AD: " + НовыйПользователь.ПолноеИмя + " (" + НовыйПользователь.Имя + ")", УровеньВажностиЖурналаСобытий.Информация);
Возврат НовыйПользователь;
Исключение
// Если возникла ошибка при записи, логируем ее.
ЗаписьЖурналаРегистрации("Ошибка при создании пользователя 1С '" + ИмяПользователя1С + "': " + ОписаниеОшибки(), УровеньВажностиЖурналаСобытий.Ошибка);
Возврат Неопределено;
КонецПопытки;
Иначе
// Пользователь найден. Можно обновить его свойства, если необходимо.
// Например, обновим полное имя, если оно изменилось в AD.
Если Пользователь1С.ПолноеИмя <> ДанныеПользователяAD.ОтображаемоеИмя Тогда
Пользователь1С.ПолноеИмя = ДанныеПользователяAD.ОтображаемоеИмя;
Попытка
Пользователь1С.Записать(); // Записываем изменения.
ЗаписьЖурналаРегистрации("Обновлено полное имя пользователя 1С из AD: " + Пользователь1С.ПолноеИмя + " (" + Пользователь1С.Имя + ")", УровеньВажностиЖурналаСобытий.Информация);
Исключение
ЗаписьЖурналаРегистрации("Ошибка при обновлении пользователя 1С '" + ИмяПользователя1С + "': " + ОписаниеОшибки(), УровеньВажностиЖурналаСобытий.Ошибка);
КонецПопытки;
КонецЕсли;
// Также можно проверить и обновить статус 'Недействителен' или другие свойства.
Если Пользователь1С.Недействителен Тогда // Если пользователь был помечен как недействительный, но появился в AD
Пользователь1С.Недействителен = Ложь;
Попытка
Пользователь1С.Записать();
ЗаписьЖурналаРегистрации("Активирован пользователь 1С '" + Пользователь1С.Имя + "' из AD.", УровеньВажностиЖурналаСобытий.Информация);
Исключение
ЗаписьЖурналаРегистрации("Ошибка при активации пользователя 1С '" + ИмяПользователя1С + "': " + ОписаниеОшибки(), УровеньВажностиЖурналаСобытий.Ошибка);
КонецПопытки;
КонецЕсли;
Возврат Пользователь1С;
КонецЕсли;
КонецФункции
// Вспомогательная функция для генерации случайного пароля (если выбран вариант 2 аутентификации)
Функция СгенерироватьСлучайныйПароль()
Символы = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()";
ДлинаПароля = 12;
СлучайныйПароль = "";
Для Инд = 1 По ДлинаПароля Цикл
ИндексСимвола = Цел(СлучайноеЧисло() * (СтрДлина(Символы) - 1)) + 1;
СлучайныйПароль = СлучайныйПароль + Сред(Символы, ИндексСимвола, 1);
КонецЦикла;
Возврат СлучайныйПароль;
КонецФункции
В приведенном коде мы сначала пытаемся найти пользователя 1С по его
Имени
sAMAccountName
ПользователиИнформационнойБазы.НайтиПоИмени()
ПользователиИнформационнойБазы
Создать()
Рассмотрим важные параметры для настройки аутентификации:
АутентификацияСтандартная = Ложь;
АутентификацияОС = Истина;
ПользовательОС = ДоменAD + "\" + ИмяПользователя1С;
Если по каким-либо причинам вы не хотите использовать аутентификацию ОС, вы можете включить
АутентификацияСтандартная = Истина;
СгенерироватьСлучайныйПароль()
ПотребуетсяСменаПароля = Истина;
Чтобы автоматизировать процесс синхронизации, мы поместим нашу логику в регламентное задание.
РегламентныеЗадания
Предположим, что все вспомогательные функции (ПолучитьПользователейИзActiveDirectory, СоздатьИлиОбновитьПользователя1С и ЗаписьЖурналаРегистрации) находятся в общем модуле, например, УправлениеПользователямиИзAD, который должен иметь галки "Сервер" и "Внешнее соединение". Эти флаги гарантируют, что код модуля может быть вызван на сервере 1С и доступен из внешних COM-соединений, если это потребуется.
// В общем модуле "УправлениеПользователямиИзAD" с флагами "Сервер" и "Внешнее соединение"
// (можно также установить 'Клиент (обычное приложение)' и 'Клиент (управляемое приложение)' для удобства отладки,
// но для регламентного задания на сервере это не обязательно)
Процедура СинхронизироватьПользователейСActiveDirectory() Экспорт
ДоменAD = "ваш_домен.локал"; // Обязательно укажите имя вашего домена! Например, 'mycompany.local'
ЗаписьЖурналаРегистрации("Начало синхронизации пользователей с Active Directory...", УровеньВажностиЖурналаСобытий.Информация);
СписокПользователейAD = ПолучитьПользователейИзActiveDirectory(ДоменAD);
Если СписокПользователейAD = Неопределено Тогда
ЗаписьЖурналаРегистрации("Синхронизация отменена из-за ошибок получения данных из AD. Проверьте права и доступность доменного контроллера.", УровеньВажностиЖурналаСобытий.Ошибка);
Возврат;
КонецЕсли;
Для Каждого ПользовательAD Из СписокПользователейAD Цикл
Попытка
СоздатьИлиОбновитьПользователя1С(ПользовательAD, ДоменAD);
Исключение
ЗаписьЖурналаРегистрации("Ошибка при обработке пользователя AD '" + ПользовательAD.SAMAccountName + "': " + ОписаниеОшибки(), УровеньВажностиЖурналаСобытий.Ошибка);
КонецПопытки;
КонецЦикла;
// Дополнительный шаг: деактивация пользователей 1С, которых нет в AD
// Этот шаг требует более сложной логики и должен быть реализован
// с осторожностью, чтобы избежать случайной деактивации.
// Пример:
// ДеактивироватьНесуществующихПользователей1С(СписокПользователейAD);
ЗаписьЖурналаРегистрации("Синхронизация пользователей с Active Directory завершена.", УровеньВажностиЖурналаСобытий.Информация);
КонецПроцедуры
// Вспомогательная процедура для записи сообщений в журнал регистрации 1С.
// Позволяет отслеживать ход выполнения регламентного задания и выявлять ошибки.
Процедура ЗаписьЖурналаРегистрации(ТекстСообщения, УровеньВажности)
ЖурналРегистрации.Записать(ТекстСообщения, УровеньВажности);
КонецПроцедуры
Теперь эту процедуру
СинхронизироватьПользователейСActiveDirectory
УправлениеПользователямиИзAD.СинхронизироватьПользователейСActiveDirectory
Рекомендуется выполнять регламентное задание на сервере 1С:Предприятия. Критически важно убедиться, что учетная запись, под которой запущен сервер 1С, имеет необходимые права для чтения данных из Active Directory. Если 1С-сервер работает под учетной записью
Local System
При внедрении такой синхронизации могут возникнуть следующие нюансы и вопросы, которые мы должны рассмотреть для обеспечения стабильной и корректной работы системы:
yourcompany.local
ПолучитьПользователейИзActiveDirectory
ПользовательОС
СтрокаЗапроса
""
СоздатьИлиОбновитьПользователя1С
Недействителен = Истина
ГруппыПользователей
Эффективная интеграция с Active Directory значительно упрощает администрирование пользовательских учетных записей в 1С и повышает общую безопасность системы за счет централизованного управления доступом. Следуя этим рекомендациям и примерам кода, вы сможете создать надежное и автоматизированное решение для вашей информационной системы 1С.