powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / работа с ADSI
17 сообщений из 17, страница 1 из 1
работа с ADSI
    #36379802
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пытаюсь из Delphi работать с ADSI (Active Directory Service Interfaces).
Задача: Добавить существующего пользователя виндоуз в указанную группу пользователей. Например, в виндоуз на локальном компе создан пользователь с именем user1, нужно программно из Delphi добавить user1 в группу "Администраторы" данного компа, т.е. сделать user1 локальным админом.
Я пока сделал следующее:
Импортировал в Delphi стандартную библиотеку типов ADSI (activeds.tlb), содержащую все необходимые для работы с ADSI интерфейсы любезно предоставленные Microsoft. Далее у себя в приложении создаю COM-объекты, чтобы дёргать за эти интерфейсы. Научился уже создавать пользователя, задавать ему пароль, ставить галочку его атрибуту "Срок действия пароля не ограничен". Делаю это так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
 uses  ActiveX,   // используется для COM Moniker stuff... 
ActiveDs_TLB,  // созданная библиотека типов 
ComObj;          // используется для OleCheck и других функций COM 

 implementation 

 function  TForm1.GetObject( const  name:  string ): IDispatch;
 var 
  Moniker: IMoniker;
  Eaten: integer;
  BindContext: IBindCtx;
  Dispatch: IDispatch;
 begin 
  OleCheck(CreateBindCtx( 0 , BindContext));
  OleCheck(MkParseDisplayName(BindContext, PWideChar(WideString(name)), Eaten, Moniker));
  OleCheck(Moniker.BindToObject(BindContext,  nil , IDispatch, Dispatch));

  Result := Dispatch;
 end ; 

 // Создание пользователя 
 procedure  TForm1.BtnCreateUserClick(Sender: TObject);
 var 
  Usr: IADsUser;
  Comp: IADsContainer;
 begin 
   try 
    Comp := GetObject('WinNT://home,computer')  as  IADsContainer;
    Usr := Comp.Create('user', 'user1')  as  IADsUser;
    Usr.SetInfo;
   except 
    on E: EOleException  do 
     begin 
      ShowMessage(E.message);
     end ;
   end ;
 end ;

 // задание пользователю пароля 
 procedure  TForm1.BtnSetPasswordClick(Sender: TObject);
 var 
  Usr: IADsUser;
 begin 
   try 
    Usr := GetObject('WinNT://home/user1,user')  as  IADsUser;
    Usr.SetPassword('12345');
   except 
    on E: EOleException  do 
     begin 
      ShowMessage(E.message);
     end ;
   end ;
 end ;

 // проверка и изменение атрибута пользователя "Срок действия пароля не ограничен" 
 procedure  TForm1.BtnNeverExpiresClick(Sender: TObject);
 var 
  Usr: IADsUser;
 begin 
   try 
    Usr := GetObject('WinNT://home/user1,user')  as  IADsUser;

     // Проверяем состояние чекбоксов... 
     // 65536 объявлено как UF_DONT_EXPIRE_PASSWORD в iads.h 
     // в ADSI SDK от Microsoft 
     if  (Usr.Get('UserFlags')  and   65536 )  then 
      Usr.Put('UserFlags', Usr.Get('UserFlags')  xor   65536 )
     else 
      Usr.Put('UserFlags', Usr.Get('UserFlags')  or   65536 );

    Usr.SetInfo;

   except 
    on E: EOleException  do 
     begin 
      ShowMessage(E.message);
     end ;
   end ;
 end ;

А как добавить пользователя к заданной группе?
...
Рейтинг: 0 / 0
работа с ADSI
    #36379869
Фотография Альт
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В пределах вашего кода примерно так:
Код: plaintext
1.
2.
3.
Group := GetObject( ... )  as  IADsGroup;
User := GetObject( ... )  as  IADsUser;
Group.Add( User.ADsPath );
Group.SetInfo;

еще можно сразу создать пользователя в нужной группе.
...
Рейтинг: 0 / 0
работа с ADSI
    #36379883
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Альт,

да, получилось, спасибо!

Только почему то не срабатывает проверка на существование ((

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
 procedure  TForm1.BtnAddUserClick(Sender: TObject);
 var 
  Grp: IADsGroup;
 begin 
   try 
    Grp := GetObject('WinNT://home/Администраторы,group')  as  IADsGroup;

     if  Grp.IsMember('WinNT://home/user1,user')  then 
      showmessage('Пользователь уже есть в группе')
     else 
      Grp.Add('WinNT://home/user1,user');    

    Grp.SetInfo;
   except 
    on E: EOleException  do 
     begin 
      ShowMessage(E.message);
     end ;
   end ;
 end ;

Что не так?
...
Рейтинг: 0 / 0
работа с ADSI
    #36380148
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
И ещё:

А как нужно изменить мой код, чтобы он работал не только локально?
Я теперь хочу создать не просто локального пользователя, а создать пользователя домена.
В MSDN написано, что для этого нужно использовать не WinNT провайдера, а LDAP провайдера, но приведён пример создания только локального пользователя.

получить пользователя предлагается вот таким образом:
локального:
GetObject("WinNT://MYCOMPUTER/jeffsmith,user")
доменного:
GetObject("LDAP://CN=JeffSmith,OU=Sales,DC=Fabrikam,DC=Com")

Что здесь есть что?

CN=JeffSmith=имя пользователя
OU=Sales=???
DC=Fabrikam=???
DC=Com=???

Локально пользователь создаётся следующим образом:

Dim comp As IADsContainer
Dim usr as IADsUser

Set comp = GetObject("WinNT://MyMachine,Computer")
Set usr = mach.Create("user","jeffsmith")

а доменный как?
...
Рейтинг: 0 / 0
работа с ADSI
    #36380746
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Один вопрос отпал:
проверка на существование пользователя в группе срабатывает, наверное ошибся в названии группы.
...
Рейтинг: 0 / 0
работа с ADSI
    #36381872
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вроде как разобрался:

DC - соответствует домену, указывается от вложенного элемента к корню, т.е. если домен задан так: mycompany.ru, то в LDAP нужно писать так: DC=mycompany,DC=ru
OU - соответствует организационной единице (Organizational Unit), указывается от вложенного элемента к корню, т.е. если у нас в AD есть орг.един. departments, в которой собраны все подразделения компании, например: sales, it, administration, которые в свою очередь тоже орг.единицы только вложенные, то в LDAP нужно писать так: OU=sales,OU=departments
CN - соответствует контейнеру (Container), другими словами группе пользователей домена, указывается от вложенного элемента к корню, т.е. если у нас в AD есть контейнер Users, а в нём есть пользователь Vasya, то в LDAP нужно писать так: CN=Vasya,CN=Users

Мне нужно получить доступ к пользователю домена Vasya в группе-контейнере Users домена mycompany.ru на сервере serv
Я написал такой путь:
LDAP://serv/CN=Vasya,CN=Users,DC=mycompany,DC=ru
сработало прекрасно! Объект я получил.
Далее, я попытался получить доступ к пользователю Petr, который не в контейнере, а в организационной единице departments.sales домена mycompany.ru на сервере serv
Я написал такой путь:
LDAP://serv/CN=Petr,OU=sales,OU=departments,DC=mycompany,DC=ru
не сработало (((

В чём может быть дело?
...
Рейтинг: 0 / 0
работа с ADSI
    #36382188
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
История получила своё продолжение:

Как оказалось
LDAP://serv/CN=Petr,OU=sales,OU=departments,DC=mycompany,DC=ru
работает прекрасно.
Дело совершенно не в том где пользователь находится в контейнере или в организационной единице, а в использовании в пути имени пользователя. Оказывается, Petr - это свойства пользователя First Name + ' ' +Initials +'. '+Last Name в AD. А я думал, что Petr - это Logon Name. Т.е. на самом деле нужно писать так:
LDAP://serv/CN=Petr Ivanov,OU=sales,OU=departments,DC=mycompany,DC=ru
и тогда объект будет успешно получен.

Беда в том, что я заведомо знаю только Logon Name пользователя, т.е. теперь задача сводится к тому чтобы по Logon Name получить First Name, Initials и Last Name. Я думаю, нужно использовать интерфейс IADsADSystemInfo...
Подскажите как лучше это сделать? Как пробежаться по всем пользователям и по известному logon name получить First Name, Initials и Last Name? может я в чём то заблуждаюсь?
...
Рейтинг: 0 / 0
работа с ADSI
    #36382317
Noskov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Поиск реализуется через IDirectorySearch. Только декларацию его методов и используемых структур в сверяй с MSDN. ActiveDs_TLB экспортируется с ошибками.
...
Рейтинг: 0 / 0
работа с ADSI
    #36384071
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Noskov,

Эх, мне бы пример кода на делфи, работающего с IDirectorySearch. Например, как с помощью IDirectorySearch просканировать всех пользователей по заданному LDAP-пути:
LDAP://serv/OU=sales,OU=departments,DC=mycompany,DC=ru
???
...
Рейтинг: 0 / 0
работа с ADSI
    #36384112
verter
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
просканировать - это перебрать всех пользователей по указанному пути, найти среди них пользователя по заданному logon name и получить у этого найденного пользователя FullName.
...
Рейтинг: 0 / 0
работа с ADSI
    #36450630
g_Shadow
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
verter,

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
function Enum_AD_Node(Srv_IP, Domain_Name, Node_: WideString;
                      UseCred: BOOL;
                      ADsUser, ADsPass: WideString;
                      ObjectClass: WideString;
                      Attribute: WideString;
                      out ResStr:TStrings):HRESULT;
var
   fADCont      : IADsContainer;
   ADEnum       : IEnumVariant;
   ADItemsCount : ULONG;
   ADItem       : IADs;
   ADVArr       : OleVariant;
   hRes         : HRESULT;
   ADsPath      : WideString;
   S            : WideString;
begin
 CoInitialize(nil);
 Result := S_FALSE;

  if Node_ <> '' then Node_ := Node_ + ',';
  if Domain_Name = '' then
    ADsPath := WideString('LDAP://'+ Srv_IP)
  else
    ADsPath := WideString('LDAP://'+Srv_IP+'/'+Node_+'DC='+Domain_Name);

 if UseCred then
  begin
    if Failed(ADsOpenObject(ADsPath,
                            ADsUser, ADsPass,
                            ADS_SECURE_AUTHENTICATION,
                            IADsContainer,
                            fADCont
                           )
             ) then
                  begin
                   Result := S_FALSE;
                   Exit;
                  end;
  end
 else
  begin
    if Failed(ADsGetObject(ADsPath,
                           IADsContainer,
                           fADCont
                          )
             ) then
                  begin
                   Result := S_FALSE;
                   Exit;
                  end;
  end;

  if Failed (ADsBuildEnumerator ( fADCont, @ADEnum )) then
    begin
     Result := S_FALSE;
     Exit;
    end;

  while SUCCEEDED (hRes) do
    begin
      hRes := ADsEnumerateNext(ADEnum, 1, ADVArr, ADItemsCount);
      if ( ADItemsCount <= 0 ) then Break;

       IDispatch (ADVArr).QueryInterface(IADs, ADItem);
       try
          S := Format('%s', [ADItem.Class_]);
          if ObjectClass = '' then
            S := UpperCase(Format('%s;%s'{;%s;'}, [ADItem.Get(Attribute), ADItem.ADsPath{, ADItem.Class_}]))
          else
          if S = Variant(ObjectClass) then
            S := UpperCase(Format('%s;%s'{;%s;'}, [ADItem.Get(Attribute), ADItem.ADsPath{, ADItem.Class_}]))
          else
            S := '';
       finally
        if S <> '' then
          ResStr.Add(S);
       end;
    end;
  Result := S_OK;
 CoUninitialize;
end;

//Вызов:
      DC_List := TStringList.Create;
      ObjectClass := 'computer'; 
      Attribute := 'name';
        Enum_AD_Node(Server,
                     Domain,
                     'CN=Computers',
                     false, User, Pass,
                     ObjectClass,
                     Attribute,
                     DC_List);
        ListBox1.Items.AddStrings(DC_List);
        DC_List.Free;
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
работа с ADSI
    #38308600
88adim
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мне нужно в Delphi зачитать объекты AD с определенным набором атрибутов. Когда брать простые атрибуты типа Name, Mail, OU, и т.д. проблем никаких - VarToStr - и все ок. Но я столкнулся с серьезной проблемой. Часть атрибутов в запросе более сложных форматов, например Array of Variant (с этим я разобрался) и Dispatch (вот этот никак не могу осилить) как его распарсить в Delphi и получить конкретное стороковое значение.
Работаю через ADOQuery. Запрос простой, выглядит так:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
SELECT
objectClass
,objectCategory
,objectGUID
,objectSid
,cn
,ou
,whenCreated
,whenChanged
,Name
,distinguishedName
,ADsPath
,description
,sAMAccountName
,userPrincipalName
,mail
,otherMailbox
,proxyAddresses
,dNSHostName
,operatingSystem
,operatingSystemVersion
,operatingSystemServicePack
,pwdLastSet
,badPwdCount
,badPasswordTime
,localPolicyFlags
,accountExpires
,lastLogoff
,lastLogon
,logonCount
FROM
'LDAP://DC=sidomain,DC=local'
WHERE objectClass='user' or objectClass='computer'



Подскажите, плиз, как получить значение атрибута, например pwdLastSet.
Спасибо.
...
Рейтинг: 0 / 0
работа с ADSI
    #38308991
88adim
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Удалось решить проблему, почитав форум. Может еще кому пригодится:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
function TForm1.DispatchToString(fld: TField): String;
var
 VarValue  : OleVariant;
 ov_i64 : OleVariant;
 i64 : IADsLargeInteger;

 UTC_Time,
 Loc_Time : TFileTime;
 Sys_Time : TSystemTime;
 TimeZone : TTimeZoneInformation;
 DT       : TDateTime;
begin
  VariantInit(ov_i64);
    ov_i64 := fld.Value;
      if VarIsNULL(ov_i64) then exit;
     IDispatch(ov_i64).QueryInterface(IADsLargeInteger, i64);
      DT := EncodeDate(1601,1,1);
        UTC_Time.dwHighDateTime := i64.HighPart;
        UTC_Time.dwLowDateTime  := i64.LowPart;
      if FileTimeToLocalFileTime(UTC_Time, Loc_Time) then
        if FileTimeToSystemTime(Loc_Time, Sys_Time) then
             DT := SystemTimeToDateTime(Sys_Time);
      VarValue := DateTimeToStr(DT);
  VariantClear(ov_i64);
  Result := VarValue;
end;
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
работа с ADSI
    #38694095
Фотография Kast2K
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вдруг кому пригодится для получения информации о пользователе.
...
Рейтинг: 0 / 0
работа с ADSI
    #38694536
prog123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
При попытке установить в 7-ю Дельфю, говорит - ADSI.pas(1): Program or unit 'ADSI.pas' recursively uses itself
...
Рейтинг: 0 / 0
работа с ADSI
    #38694624
Фотография Kast2K
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
prog123,

Для Delphi 7
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
работа с ADSI
    #39790674
oleg-jaguar
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Kast2K,

Можно в скомпиленом виде?
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / работа с ADSI
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]