Работа с 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 не будет опубликован. Обязательные поля помечены *