Работа с Active Directory из 1С

Пример работы с AD из 1С

Всем доброго времени суток.
Возникла необходимость работы с AD из 1с в частности создавать пользователей, группы безопасности и организационные единицы.
Нижеприведенный код распологается во общем модуле.
Для работы используется LDAP провайдер ADSI
Пример содержимого передаваемых и возвращаемых переменных:

  • Организационная единица: «OU=TestOU,DC=domain,DC=internal»
  • Группа безопасности: «CN=TestGroup,OU=TestOU,DC=domain,DC=internal»
  • Пользователь: «CN=TestUser,OU=TestOU,DC=domain,DC=internal»

SID пользователя и группы возвращается в виде COMSafeArray. Я его преобразовывавю в читаемый вид. Он мне нужен для установки прав на каталоги и файлы.
Все функции возвращают результат выполнения Ложь/Истина. В случае возникновения ошибки описание возвращается в переменную ОписаниеОшибки.
Надо не забывать, что пользователь, от имени которого запускается 1с (или пользователь сервера приложения если процедуры будут запускаться на сервере) должен обладать соответствующими правами для работы с AD

Привожу на суд общественности результаты:

1) Cоздание организационной единицы

Функция СоздатьОрганизационнуюЕдиницу(
ИмяОрганизационнойЕдиницы,
ОрганизационнаяЕдиницаВладелец,
Описание,

ГруппаДелегат = "",
ПолноеИмяОрганизационнойЕдиницы = "", //возращается путь до созданой OU
ОписаниеОшибки = ""
) Экспорт
фРезультат     = Истина;
Попытка
ОрганизационнаяЕдиницаВладелецОбъект = ПолучитьCOMОбъект("LDAP://" + ОрганизационнаяЕдиницаВладелец);
ОрганизационнаяЕдиница   = ОрганизационнаяЕдиницаВладелецОбъект.Create("OrganizationalUnit", "OU=" + ИмяОрганизационнойЕдиницы);
ОрганизационнаяЕдиница.description = Описание;
Если Не ПустаяСтрока(ГруппаДелегат) Тогда
ОрганизационнаяЕдиница.managedBy = ГруппаДелегат;
КонецЕсли;

ОрганизационнаяЕдиница.SetInfo();
Исключение
фРезультат = Ложь;
ОписаниеОшибки = ОписаниеОшибки();
КонецПопытки;
Если фРезультат Тогда
ПолноеИмяОрганизационнойЕдиницы = ОрганизационнаяЕдиница.distinguishedName;
КонецЕсли;

Возврат фРезультат;
КонецФункции

2) Создание группы безопасности

Функция СоздатьГруппуБезопасности(
ИмяГруппыБезопасности,
ОрганизационнаяЕдиницаВладелец,
Описание,

ПолноеИмяГруппыБезопасности = "",
SID = "",
ОписаниеОшибки = ""
) Экспорт
фРезультат     = Истина;
//ADS_GROUP_TYPE_GLOBAL_GROUP        = 2;
//ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP  = 4;
//ADS_GROUP_TYPE_LOCAL_GROUP         = 4;
//ADS_GROUP_TYPE_UNIVERSAL_GROUP     = 8;
//ADS_GROUP_TYPE_SECURITY_ENABLED    = -2147483648;

Попытка
ОрганизационнаяЕдиницаВладелецОбъект = ПолучитьCOMОбъект("LDAP://" + ОрганизационнаяЕдиницаВладелец);
ГруппаБезопасности   = ОрганизационнаяЕдиницаВладелецОбъект.Create("Group", "CN=" + ИмяГруппыБезопасности);
ГруппаБезопасности.sAMAccountName = ИмяГруппыБезопасности;
ГруппаБезопасности.displayName  = ИмяГруппыБезопасности;
ГруппаБезопасности.description  = Описание;
ГруппаБезопасности.groupType  = -2147483646;//ADS_GROUP_TYPE_GLOBAL_GROUP + ADS_GROUP_TYPE_SECURITY_ENABLED; //глобальная группа
ГруппаБезопасности.SetInfo();
Исключение
фРезультат = Ложь;
ОписаниеОшибки = ОписаниеОшибки();
КонецПопытки;

Если фРезультат Тогда
SID = ПреобразоватьSID(ГруппаБезопасности.objectSid);
ПолноеИмяГруппыБезопасности = ГруппаБезопасности.distinguishedName;
КонецЕсли;
Возврат фРезультат;
КонецФункции

3) Создание пользователя

Функция СоздатьПользователя(
Логин,
ПарольПользователя,
ОрганизационнаяЕдиница,
АдресЭлектроннойПочты,
МенятьПарольПриВходе = Истина,
Описание,

ПутьПользователяAD = "",
SID = "",
ОписаниеОшибки = ""
) Экспорт
фРезультат     = Истина;
Попытка
ОрганизационнаяЕдиницаОбъект  = ПолучитьCOMОбъект("LDAP://" + ОрганизационнаяЕдиница);
ПользовательAD      = ОрганизационнаяЕдиницаОбъект.Create("user", "CN=" + Логин);
ПользовательAD.sAMAccountName  = Логин;
ПользовательAD.description   = Описание;
ПользовательAD.userPrincipalName = Логин + "@" + Константы.НаименованиеДомена.Получить();
Если Не ПустаяСтрока(АдресЭлектроннойПочты) Тогда
ПользовательAD.mail     = АдресЭлектроннойПочты;
КонецЕсли;
Если МенятьПарольПриВходе Тогда
ПользовательAD.pwdLastSet  = 0;
Иначе
ПользовательAD.pwdLastSet  = -1;
КонецЕсли;

ПользовательAD.SetInfo();
ПользовательAD.SetPassword(ПарольПользователя);
Исключение
фРезультат = Ложь;
ОписаниеОшибки = ОписаниеОшибки();
КонецПопытки;

Если фРезультат Тогда
SID = ПреобразоватьSID(ПользовательAD.objectSid);
ПутьПользователяAD = ПользовательAD.distinguishedName;
КонецЕсли;

Если фРезультат Тогда
ПользовательAD.AccountDisabled = Ложь;
ПользовательAD.SetInfo();
КонецЕсли;

Возврат фРезультат;
КонецФункции

В константе НаименованиеДомена находится название домена типа «domain.internal».

4) Добавление пользователя в группу

Функция ДобавитьПользователяВГруппу(
ПолноеИмяГруппыБезопасности,
ПутьПользователяAD,

ОписаниеОшибки = ""
) Экспорт
фРезультат     = Истина;
Попытка
ГруппаБезопасности   = ПолучитьCOMОбъект("LDAP://" + ПолноеИмяГруппыБезопасности);
ГруппаБезопасности.Add("LDAP://" + ПутьПользователяAD);
ГруппаБезопасности.SetInfo();
Исключение
фРезультат = Ложь;
ОписаниеОшибки = ОписаниеОшибки();
КонецПопытки;

Возврат фРезультат;
КонецФункции

5) Удаление пользователя из группы

Функция УдалитьПользователяИзГруппы(
ПолноеИмяГруппыБезопасности,
ПутьПользователяAD,

ОписаниеОшибки = ""
) Экспорт
фРезультат     = Истина;
Попытка
ГруппаБезопасности   = ПолучитьCOMОбъект("LDAP://" + ПолноеИмяГруппыБезопасности);
ГруппаБезопасности.Remove("LDAP://" + ПутьПользователяAD);
ГруппаБезопасности.SetInfo();
Исключение
фРезультат = Ложь;
ОписаниеОшибки = ОписаниеОшибки();
КонецПопытки;

Возврат фРезультат;
КонецФункции

6) Переобразование SID из массива в читаемый вид типа «S-1-5-21-3784850290-2022084444-2521399107-4676»

Функция ПреобразоватьSID(objectSid)
SID = "S-";

МассивSID = Новый Массив;
Для Каждого ЭлементSID Из objectSid Цикл
МассивSID.Добавить(ЭлементSID);
КонецЦикла;
//SID_REVISION
SID = SID + Строка(МассивSID[0]) + "-";

КоличествоДашей = МассивSID[1];

SECURITY_NT_AUTHORITY = 0;
Для Инд = 0 По 5 Цикл
SECURITY_NT_AUTHORITY = SECURITY_NT_AUTHORITY + МассивSID[2 + Инд] * Pow(2, (5 - Инд) * 8);
КонецЦикла;
SID = SID + Строка(SECURITY_NT_AUTHORITY);

Для Инд = 0 По КоличествоДашей - 1 Цикл
SID = SID + "-";
Даш = 0;
Для ИндДаш = 0 По 3 Цикл
Даш = Даш + МассивSID[8 + 4 * Инд + ИндДаш] * Pow(2, (ИндДаш) * 8);
КонецЦикла;
SID = SID + Строка(Даш);
КонецЦикла;

Возврат SID;
КонецФункции

7) Ну и бонусом привожу процедуру установки прав на каталог. Права ставятся с наследованием на нижестоящие объекты. Для установки используется утилита операционки (в моём случае win 2012 в Asure). При вызове (в толстом клиенте) у 1С теряется фокус. К сожалению как установить права другим способом (например WMI) я не нашел. Планирую копать в сторону PowerShell.

Функция УстановитьПраваНаКаталог(
ПутьККаталогу,
SIDГруппы,

Описание = "") Экспорт
фРезультат = Истина;
КодВозврата = Неопределено;
Попытка
ЗапуститьПриложение("icacls " + ПутьККаталогу + " /grant *" + SIDГруппы + ":(CI)(OI)F", , Истина, КодВозврата)
Исключение
Описание = ОписаниеОшибки();
фРезультат = Ложь;
КонецПопытки;

Если фРезультат Тогда
Если КодВозврата <> 0 Тогда
фРезультат = Ложь;
Описание = "Код возврата: " + Строка(КодВозврата);
КонецЕсли;
КонецЕсли;

Возврат фРезультат;
КонецФункции

Список использованных ссылок:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa772203%28v=vs.85%29.aspx

http://www.script-coding.com/Python/LDAP.html

http://blogs.msdn.com/b/oldnewthing/archive/2004/03/15/89753.aspx

http://technet.microsoft.com/ru-ru/library/cc753525%28v=ws.10%29.aspx

Незаменимой вешью оказалась утилита Марка Руссиновича

Active Directory Explorer (AD Explorer)

38 Comments

  1. poyson

    Большое спасибо. Будем пробовать….

    Reply
  2. EvgeniuXP

    права на папки можно через командную строку установить — сам не раз так делал, только какая команда сейчас не вспомню, т.к. делал лет 5 назад…

    Reply
  3. ksuman

    Наш суд общественности — самый гуманный суд в мире!

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

    Все эти операции: удалить, добавить, назначить права — отлично делаются специальными инструментами администрирования.

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

    Reply
  4. migele

    (3) ksuman,

    Ну на счет практического использования это Ваше сугубо субъективное мнение.

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

    Reply
  5. stanru1

    Спасибо за потраченное время! Я тоже проводил похожие исследования для импорта пользователей из AD, но с созданием так и не хватило терпения разобраться 🙂 Обязательно в будущем воспользуюсь идеями!

    Reply
  6. Gulf_Stream

    (3) ksuman, немного не согласен, вы видимо не администрировали АД с хотя бы 1000 пользователей. На основании этого кода очень удобно выполнять массовые действия над учетными записями. Тоже накатал под свои нужды конфу для работы с LDAP но выложить на общее обозрение не рискну ибо код там уж больно неряшлив 🙁

    Reply
  7. user_2010

    Нужно в 1С.ЗУП для физических лиц формировать сетевое имя пользователя, e-mail, а потом уже глобальная задача: создавать пользователя в AD.

    Вот вопрос: я сформирую для физ лица сетевое имя — мне же нужно проверить, что такого пользователя еще нет в AD. Как это сделать?

    Reply
  8. Mi4man

    (7) user_2010, для этого необходимо выполнить запрос

    ТекстЗапроса = «SELECT ADsPath FROM ‘LDAP://»+Домен+»‘ WHERE objectClass=’User’ AND userPrincipalName='»+ДоменноеИмя+»‘»;

    Если результат пустой, то соответственно в базе нет такого юзера.

    Reply
  9. Mi4man

    (4) есть ли у Вас реализация поиска по ГУИДу в запросе?

    Reply
  10. brr

    (9) Mi4man, SELECT ADsPath FROM ‘LDAP://10.0.0.254’ WHERE objectGUID=’4e1e1853a15ac4b816db44051411­’

    Reply
  11. Mi4man

    (10) brr, что-то я пробовал, но никак не получалось. Эх…

    Reply
  12. brr

    (11) Mi4man, а ГУИД именно такое имел представление? Пример: b4e1e1853a15ac4b8106db44b0051411.

    Reply
  13. Mi4man

    (12) brr, нет, вот такое: 4e1e1853a15ac4b816db44051411 (строка).

    А надо было просто b4e1e1853a15ac4b8106db44b0051411 ?

    Reply
  14. KroVladS

    гдеб найти примеры работы с openLDAP из под Linux?

    Reply
  15. harmer

    Спасибо.

    Небольшое замечание: SID формируется с символами НПП.

    SID    = SID + Строка(Даш);
    Reply
  16. brr

    (13) Mi4man, нет, просто я не понял какое представление ГУИД вы использовали

    Reply
  17. It-developer

    Не получается создать пользователя. Пишет «Ошибка получения объекта COM: Сервер возвратил ссылку». Что может быть?

    Вроде все по схеме

    element_group = ПолучитьCOMОбъект(«LDAP://OU=Папка 2,OU=Пользователи домена,OU=sim,DC=dc,DC=my_company,DC=com)»);

    Reply
  18. jake_qwert

    А каким кодом можно изменить организационную единицу (OrganizationalUnit) у существующего пользователя? Т.е. мне нужно перенести в другую папку пользователя. Пробывал изменить distinguishedName, выдает ошибку.

    Reply
  19. jake_qwert

    Разобрался сам, вот так, если кому-то интересно (первая строка куда перемещаем, вторая откуда)

    set cont = GetObject(«LDAP://dc=dom,dc=com»)

    set newobj = cont.MoveHere(«LDAP://cn=jeffsmith,ou=sales,dc=dom,dc=com», «cn=jeffsmith»)

    Reply
  20. i_rebel

    Как получить список групп пользователя из AD ? хочу открывать пользователя AD и видеть его группы AD? ; нужно видеть все группы и те которые присвоены данному пользователю

    Reply
  21. Armando

    (20) i_rebel, код на vbs для получения своих групп:

    Set objADSysInfo = CreateObject(«ADSystemInfo»)
    Set objUser = GetObject(«LDAP://» & objADSysInfo.UserName)
    For Each objGroup in objUser.memberOf
    NameGroup = GetObject(«LDAP://» & objGroup).cn
    Next
    Reply
  22. imbaZeratul

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

    Reply
  23. i_rebel

    (21) Armando, мне нужно все группы, чтобы потом мог сделать интерактивно добавление или удаление из группы

    Reply
  24. jake_qwert

    Все получилось всем спасибо.

    Reply
  25. Danila-Master

    Весь инет перерыл, так и не смог найти нормального примера/описания как переименовать пользователя в AD.

    Задача такая:

    В 1С сотрудник сменил ФИО, зная ее доменное имя, как поменять на новое ФИО в самом AD?

    Reply
  26. blackhole321

    (25) Danila-Master,

    Попробуйте через PowerShell. Set-ADUser (https://technet.microsoft.com/en-us/library/ee617215.aspx)

    Reply
  27. Danila-Master

    Помогите разобтаться. Почему-то не хочет работать powershell.

    В 1С делаю ЗапуститьПриложение(«powershell.exe -Command {Set-ADUser -Identity «»1ctest»» -Surname «»Иванов»» -GivenName «»Иван»»}»);

    Имя учетни не меняется.

    В cmd.exe пишу строку: powershell.exe -Command {Set-ADUser -Identity «1ctest» -Surname «Иванов» -GivenName «Иван»}

    Так же, имя учетки не меняется.

    Но если в самой консоле PowerShell’а написать строку: Set-ADUser -Identity «1ctest» -Surname «Иванов» -GivenName «Иван»

    То все ништяк, Имя в учетке поменялось.

    1С, cmd и консоль PowerShell запущенны от именю учетки, у которой есть права на изменение данных в домене. Так что этот момент стразу отсекаю.

    Reply
  28. blackhole321

    (27) Danila-Master, в 1С код выполняется на том же компьютере, где Вы открывали консоль PowerShell? (М.б. Вы открывали консоль на своей клиентской машине, а ЗапуститьПриложение выполняли на сервере)

    Reply
  29. Danila-Master

    (28) blackhole321,

    (М.б. Вы открывали консоль на своей клиентской машине, а ЗапуститьПриложение выполняли на сервере)

    Так оно и есть и будет. Но запуск происходит от одной и той же учетки, что на моем компе, что на сервере.

    Но перед тем как писать в 1С, мне нужно из командной строки добится результата. Поэтому пока делаю все на своем компе.

    Столкнулся с интересной ситуацией:

    В cmd пишу powrshell.exe, проваливаюсь в сам сонсоль PS.

    Далее в PS пишу: Get-ADUser -Identity «1ctest»

    Выдает ошибку, что команда Get-ADUser не найдена.

    Как оказалось нужно подключить модуль для работы с AD.

    > Import-Module ActiveDirectory

    > Get-ADUser -Identity «1ctest»

    о чудо, Get-ADUser выдал результат. но при этом, даже после подключения модуля, команда Set-ADUser -Identity «1ctest» -Surname «Иванов» -GivenName «Иван»

    так и выполнилась. И ошибок нет.

    Reply
  30. Danila-Master

    В общем разобрался я 🙂

    1. Нужно поставить PowerShell версии 4.0 (по-умолчанию у windows 7 и 2008 R2 стоит версия 2.0)

    В версии 4.0 не нужно импортировать разные модули, shell сама все делает.

    2. Рабочая команда в cmd: powershell.exe Set-ADUser -Identity ‘1ctest’ -Surname ‘Иванов’ -GivenName ‘Иван’ -DisplayName ‘Иванов Иван’

    где

    Identity — параметр поиска (в данном случае доменное имя пользователя)

    Surname — Фамилия

    GivenName — Имя

    DisplayName — Отображаемое имя

    Reply
  31. Danila-Master

    Все, разобралися.

    Вдруг кому пригодится, вот рабочий код:

    ТекстКоманды = «powershell.exe -command «»$user = Get-ADUser -Identity ‘» + ДоменноеИмя + «‘»;
    ТекстКоманды = ТекстКоманды + » ; Set-ADUser -Identity $user -Surname ‘» + ТРег(ФамилияEn) + «‘ -GivenName ‘» + ТРег(ИмяEn) + «‘ -DisplayName ‘» + ПолноеИмя + «‘»;
    ТекстКоманды = ТекстКоманды + » ; Rename-ADObject -Identity $user -NewName ‘» + ПолноеИмя + «‘»»»;
    
    ЗапуститьПриложение(ТекстКоманды);
    
    Reply
  32. user695952_stepanyan

    Какой командой можно добавить роль в список ролей MemberOf ? И как получить ссылку на саму группу?

    Reply
  33. Neverpoint

    (3) Вы далеки от реального управления бизнесом. В частности у нас в 1С создают новых клиентов, карточки и они автоматом прописываются в AD который как LDAP раздает в FreePBX и еще ряд специальных услуг для клиентов пароли и юзернеймы. Вы не понимаете о чем речь

    Reply
  34. burstmashine

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

    Reply
  35. Danila-Master

    Можно так проверить:

    objConnection = Новый COMОбъект(«ADODB.Connection»);
    objConnection.Provider = «ADSDSOObject»;
    objConnection.Open(«Active Directory Provider»);
    Домен1 = «dm»;
    Домен2 = «bc»;
    ДоменноеИмя = «Ivanov_Ivan»;
    
    query = «<LDAP://DC=» + Домен1 + «,DC=» + Домен2 + «>;(&(objectCategory=person)(objectClass=user)(sAMAccountName=» + ДоменноеИмя + «));name,AdsPath,sAMAccountName,mail,pwdLastSet; SubTree»;
    
    objRS = objConnection.Execute(query);
    
    Если Не rs.EOF Тогда
    Сообщить(«Пользователь существует»);
    КонецЕсли;
    
    conn.Close();

    Показать

    Reply
  36. burstmashine

    Ещё раз сдравствуйте, а как добавить пользователей в группу, по пункту 4 не получается .Все пользователи в группе Учащиеся, в ней группа Студенты с подпиской. я пробывал и так и сяк, что не так делаю?)

    Reply
  37. triviumfan

    Может вы в курсе почему стандартный метод ПользователиОС() возвращает массив структур доменов с ограничением массива пользователей в 100 элементов?

    Reply
  38. MSK_Step

    (0) Для автора, преобразование objectGUID в строку, может кому пригодиться

    //https://support.microsoft.com/en-us/help/325649/how-to-retrieve-the-guid-of-an-object-from-the-active-directory

    МассивГУИД = ПреобразоватьВМассивГУИДНаСервере(ПользовательAD.objectGUID);
    
    СтрокаGUID  = СтрСоединить(МассивГУИД,»»);
    СтрокаGUID = ConvertHexStringGUIDToStringGUID(СтрокаGUID);
    

    Функция ConvertHexStringGUIDToStringGUID(strOctet = «»)
    
    tmpGUID = «»;
    GUIDStr = «»;
    
    GUIDStr = Mid(strOctet, 7, 2);
    GUIDStr = GUIDStr + Mid(strOctet,  5, 2);
    GUIDStr = GUIDStr + Mid(strOctet,  3, 2);
    GUIDStr = GUIDStr + Mid(strOctet,  1, 2);
    GUIDStr = GUIDStr + Mid(strOctet, 11, 2);
    GUIDStr = GUIDStr + Mid(strOctet,  9, 2);
    GUIDStr = GUIDStr + Mid(strOctet, 15, 2);
    GUIDStr = GUIDStr + Mid(strOctet, 13, 2);
    GUIDStr = GUIDStr + Mid(strOctet, 17, СтрДлина(strOctet));
    
    tmpGUID = «{» + Mid(GUIDStr,  1,  8) + «-» + Mid(GUIDStr,  9, 4)
    + «-» + Mid(GUIDStr, 13,  4) + «-» + Mid(GUIDStr, 17, 4)
    + «-» + Mid(GUIDStr, 21, 15) + «}»;
    
    Возврат tmpGUID;
    КонецФункции
    

    Показать

    Функция ПреобразоватьВМассивГУИДНаСервере(objectGUID)
    
    МассивGUID    = Новый Массив;
    Для Каждого ЭлементGUID Из objectGUID Цикл
    МассивGUID.Добавить(DecToAny(ЭлементGUID,16));
    КонецЦикла;
    
    Возврат  МассивGUID;
    КонецФункции
    
    

    Показать

    Функция DecToAny(Знач тЗначение, Знач тОснование)
    тРезультат = «»;
    
    Пока тЗначение > 0 Цикл
    тРезультат = Сред(«0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ», тЗначение%тОснование + 1, 1) + тРезультат;
    тЗначение = Цел(тЗначение/тОснование) ;
    КонецЦикла;
    
    Возврат тРезультат;
    КонецФункции
    
    

    Показать

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *