Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

NetAPI - Работа с пользователями в группах


Информационный Канал Subscribe.Ru

Программирование для Windows NT на Delphi
Выпуск номер : 3 [ 23 июля 2002 года]


Здравствуйте, уважаемые подписчики.

Сначала о хорошем - рассылку перевели в категорию "Обычные", с чем я себя и вас поздравляю. Теперь о плохом - об ошибке, в выпуске за 8 июля.
[...]
в функции AddUser допущена ошибка, вместо строк
 lpwSrv := Str2Wide(Comp.Text); // Имя компьютера
 Res:= NetUserAdd(nil,1,@User,@dwErr);
должно быть
 lpwSrv := Str2Wide(Comp.Text); // Имя компьютера
 Res:= NetUserAdd(lpwSrv,1,@User,@dwErr);
(то есть в качестве имени сервера передаем имя, а не nil)

С уважением, Дмитрий Заузолков
Спасибо Дмитрию за замечание. Ошибка получилась из за того, что в исходниках было или nil или имя сервера, и переделывалось все уже при подготовке рассылки. Пришу извенить за подобные ошибки.

Сегодня в рассылке:
- Управление пользователями в группах
- Работа в контексте залогиненого пользователя из сервиса



Статья
Управление пользователями в группах


В прошлом выпуске мы уже затронули две функции для работы с пользователями в группах - NetGroupAddUser и NetGroupDelUser (для добавления и удаления пользователя в глобальную группу). Третья функция NetGroupGetUsers получает список пользователей глобальной группы.

В параметрах функций - lpwServer - сервер, на котором находится группа lpwGroup. lpwUser - соответственно пользователь.

uses LmApiBuf,LmAccess;

procedure EnumGroupUsers(lpwServer,lpwGroup: PWideChar);
type
 TUserGroupArray = array of GROUP_USERS_INFO_0;

const
 // Размер выделяемого буфера
 PREF_LEN = 1024;

var
 pBuffer:Pointer; // Указатель на наш буфер
 Res,dwRead,dwTotal,hRes,i:Cardinal;
begin
 hRes:=0;
 ListBox1.Clear; //Будем выводить результат в ListBox, а пока очищаем его

 repeat
  // Перечисляем пользователей группы lpwGroup на компютере lpwServer
  Res:=NetGroupGetUsers(lpwServer,lpwGroup,0,pBuffer,PREF_LEN,dwRead,dwTotal,@hRes);
  //В случае удачи результат может быть или ERROR_SUCCESS (все данные получены)
  // или ERROR_MORE_DATA - будем еще перечислять
  if (Res=0) or (Res=ERROR_MORE_DATA)
   then begin
    for i:=0 to dwRead-1 do
  // Вывод списка
     ListBox1.Items.Add(TUserGroupArray(pBuffer)[i].grui0_name);
    NetApiBufferFree(pBuffer);
   end;
 until Res<>ERROR_MORE_DATA;

end; 
Теперь узнаем, в каких группах находится определенный пользователь. Для этого можно перечислить всех пользователей во всех группах, и найти те, в которых есть нужный нам пользователь (видел я такую реализацию :)) Но мы воспользуемся функциями NetUserGetGroups и NetUserGetLocalGroups.

procedure GetUserGroups(lpwServer,lpwUser : PWideChar);
type
 TUserGroupArray = array of GROUP_USERS_INFO_0;
var
 Res : Cardinal;
 pBuffer : Pointer;
 dwRead,dwTotal,i:Cardinal;
begin
 Res:=NetUserGetGroups(lpwServer,lpwUser,0,pBuffer,Cardinal(-1),dwRead,dwTotal);
 if Res<>0 then Exit;

 for i:=0 to dwRead-1 do
  ListBox1.Items.Add(TUserGroupArray(pBuffer)[i].grui0_name);

 NetApiBufferFree(pBuffer);
end;

Ну, тут все просто и понятно - получили массив групп и их кол-во, вывели их и очистили буфер. Это мы получили список глобальных групп, в которых находится пользователь. Теперь получим локальные

procedure GetUserLocalGroups(lpwServer,lpwUser : PWideChar);
type
 TLocalGroupArray = array of LOCALGROUP_USERS_INFO_0;
var
 pBuffer : Pointer;
 cbRead,cbTotal,Res,i : Cardinal;
begin

 Res:= NetUserGetLocalGroups(lpwServer,lpwUser,0,LG_INCLUDE_INDIRECT ,
                                 pBuffer,Cardinal(-1),cbRead,cbTotal);
 if Res<>0 then Exit;

 ListBox1.Clear;
 for i:=0 to cbRead-1 do
   ListBox1.Items.Add(TLocalGroupArray(pBuffer)[i].lgrui0_name);

 NetApiBufferFree(pBuffer);
end;

Эта функция очень похожа на NetUserGetGroups, за исключением 4 параметра dwFlag. Пока определено только одно значение - LG_INCLUDE_INDIRECT, позволяющее выводить те группы, в которых пользователь явно не находится. Например, пользователь User находится в локальной группе Users и глобальной GlobalUsers. В локальную группу Administrators входит и глобальная группа GlobalUsers. Если параметр равен LG_INCLUDE_INDIRECT, то функция вернет не только группу Users, но и группу Administrators, в которой пользователь явно не указан. А если этот параметр равен нулю... в общем, на NT4Server эта программа работать не захотела :))

Осталось посмотреть работу с пользователями локальных групп. Сначала добавление или удаление пользователей и группы.
procedure AddDelUsersToGroup(lpwServer,lpwGroup : PWideChar);
type
 TGroupInfoArray = array of LOCALGROUP_MEMBERS_INFO_0;
var
 pBuffer : TGroupInfoArray;
 Res     : Cardinal;
 Users : TStringList;
 i : Integer;
begin
 // Создаем TStringList (можно было его позаимствовать у другого класса)
 // И добавляем имена пользователей, которых мы добавим в группу
 Users:=TStringList.Create;
 Users.Add('user1');
 users.Add('user2');
 Users.Add('User3');

 // В этом примере у нас pBuffer (по определению) не указатель, а массив
 // Назначаем его новый размер
 SetLength(pBuffer,Users.Count);

 // Получаем SID по имени пользователя
 // Функция GetUserSid будет обсуждаться ниже
 For i:=0 to Users.Count-1 do
  begin
   GetUserSid(lpwServer,Users.Strings[i],pBuffer[i].lgrmi0_sid);
  end;

 // Добавляем пользователей
 Res:= NetLocalGroupAddMembers(lpwServer,lpwGroup,0,pBuffer,Users.Count);
 // ИЛИ удаляем их
 Res:= NetLocalGroupDelMembers(lpwServer,lpwGroup,0,pBuffer,Users.Count);
 ShowMessage(SysErrorMessage(Res));

 // Очищаем занятую функцией GetUserSid память
 For i:=0 to Users.Count-1 do
  FreeMem(pBuffer[i].lgrmi0_sid);

 Users.Free;
end;

Функции NetLocalGroup* в отличие от NetGroup* работают не с именами пользователей, а с их SID (подробнее о SID - идентификаторе безопасности, в следующем выпуске). И добавляет/удаляет не одного (в общем случае конечно :)), а несколько пользователей сразу. Для простоты и "приближения к реальным условиям" использовался TStringList. Но можно было взять и TStrings из TListBox или другого компонента. После установки размера массива pBuffer мы по очереди получили SID пользователей из списка. Функции GetUserSid в WinAPI нет, поэтому пришлось написать ее самому.

procedure GetUserSid(lpwServer: PWideChar; sUser:String; var pSid:PSID);
var
 lpDomain : PWideChar;
 cbDomain,cbSid : Cardinal;
 lpwUser : PWideChar;
 cbUser  : Cardinal;
 peUse : Cardinal;
begin
 // Переводим имя пользователя в Unicode
 cbUser := Length(sUser);
 cbUser:=(cbUser+1)*2;
 GetMem(lpwUser,cbUser);
 StringToWideChar(sUser,lpwUser,cbUser);

 cbSid:=0;
 cbDomain:=0;

 if not LookupAccountNameW(lpwServer,lpwUser,nil,cbSid,nil,cbDomain,
          peUse) and (GetLastError=122)
  then begin
   GetMem(pSid,cbSid);
   GetMem(lpDomain,cbDomain*2);
   if not LookupAccountNameW(lpwServer,lpwUser,pSid,cbSid,lpDomain,cbDomain,peUse)
    then ShowMessage('LookupAccountNameW'#13#10+SysErrorMessage(GetLastError));

   FreeMem(lpDomain);
   FreeMem(lpwUser);
  end;
end;

Что мы тут делали - первый вызов LookupAccountNameW дает нам размер буфера, необходимый для получения SID пользователя. После этого мы выделяем память под SID и имя домена и снова вызываем LookupAccountNameW. Теперь у нас в pSid должен быть указатель на SID пользователя. Имя пользователя мы переводим из String в PWideChar. В конце функции мы очищаем всю занятую нами память, кроме памяти под SID (но не забываем про нее).

Теперь вернемся снова к предыдущему примеру - в GetUserSid мы передаем по очереди все элементы массива pBuffer. После выполнения цикла у нас будет массив указателей на SIDы пользователей. И теперь мы передаем этот массив (вообще то надо указатель на массив), в NetLocalGroupAddMembers или NetLocalGroupDelMembers. Теперь очищаем занятую нами в GetUserSid память.

Для работы с локальными группами есть еще две функции NetLocalGroupGetMembers и NetLocalGroupSetMembers. Первая из них перечисляет всех пользователей локальной группы.

procedure GetLocalGroupUsers(lpwServer,lpwGroup : PWideChar);
type
 TUserArray = array of LOCALGROUP_MEMBERS_INFO_1;
var
 pBuffer : Pointer;
 cbRead,cbTotal,Res,hRes : Cardinal;
 i:Integer;
begin
 hRes:=0;
 repeat
  Res:=NetLocalGroupGetMembers(lpwServer,lpwGroup,1,pBuffer,1024,cbRead,cbTotal,@hRes);
  if (Res=0) or (Res=ERROR_MORE_DATA)
   then begin
    for i:=0 to cbRead-1 do
     ListBox1.Items.Add(TUserArray(pBuffer)[i].lgrmi1_name);
     NetApiBufferFree(pBuffer);
   end;
 until Res<>ERROR_MORE_DATA;
end;

Думаю комментировать действия нет смысла (подобные примеры уже встречались не раз). На уровне 1 функция возвращает не только SID пользователя, но и имя. Поэтому воспользуемся этим уровнем. (на уровне 2 - имя пользователя и домена)
Назначение функции NetLocalGroupSetMembers - назначать пользователей, входящих в группу (не добавлять, а назначать - т.е. остальные пользователи из группы удаляются). Я придумал всего два применения этой функции. Первое - скопировать пользователей из одной группы в другую (можно на разных компьютерах).

procedure CopyGroupMembers(lpwServer1,lpwGroup1,lpwServer2,lpwGroup2 : PWideChar);
var
 pBuffer : Pointer;
 cbRead,cbTotal : Cardinal;
begin
 if NetLocalGroupGetMembers(lpwServer1,lpwGroup1,0,pBuffer,Cardinal(-1),
                            cbRead,cbTotal,nil)=0
  then NetLocalGroupSetMembers(lpwServer2,lpwGroup2,0,pBuffer,cbRead);
end;  

Тут все просто - получаем пользователей одной группы и устанавливаем их в другой. Второй пример еще проще - удалим всех пользователей из группы
procedure ClearGroup(lpwServer,lpwGroup : PWideChar);
begin
 NetLocalGroupSetMembers(lpwServer,lpwGroup,0,nil,0)
end;
Еще раз напомню - все строки в Net функциях только в Unicode (как перевести String в Unicode можно посмотреть в начале функции GetUserSid). Заголовочные файлы можно скачать отсюда [300Kb].

Вот на этом тема пользователей и групп пока закрыта. Все вопросы пишите мне на flint@vtc.ru



Вопросы и ответы
Работа в контексте залогиненого пользователя из сервиса

Пример позволяет выполнять действия (из сервиса) под пользователем, запустившим процесс с ProcessID = dwPid
function GetUserContext(dwPid : Cardinal):Boolean;
var
 hProc  : THandle;
 hToken : THandle;
 User   : array [0..100] of Char;
 cbUser : Cardinal;
begin
 Result:=false;

 hProc:=OpenProcess(PROCESS_ALL_ACCESS,false,dwPid);
 if hProc = 0 then Exit;

 if not OpenProcessToken(hProc,TOKEN_DUPLICATE or TOKEN_QUERY  ,hToken)
  then Exit;

 if not ImpersonateLoggedOnUser(hToken)
  then Exit;

 // С этого момента код будет выполняться в
 // Контексте пользователя, под которым запущен
 // процесс  c PID = dwPID
 cbUser:=100;
 GetUserName(User,cbUser);
 ShowMessage(User);

 RevertToSelf;

 // Теперь снова работаем под системой
 cbUser:=100;
 GetUserName(User,cbUser);
 ShowMessage(User);

 Result:=true;
end;

Вот и все на сегодня.

С уважением, ведущий рассылки FliNT
delphi.xonix.ru



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное