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

Управление группами пользователей.


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


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


Здравствуйте, уважаемые подписчики.
У меня к Вам такой вопрос - в следующем выпуске я закончу тему NetAPI и вопрос - о чем писать дальше? Может сообщите, какие темы вам интересны ? ( О чем писать я конечно найду, но хотелось бы, чтобы это было интересно Вам). Также со следующего выпуска начну публиковать некоторые ваши вопросы (задать которые вы можете, написав мне)

Сегодня в рассылке:
- Управление учетными записями групп
- FAQ : список доступных привилегий
- Напоследок

Сегодня я продолжу тему NetAPI : как работать с группами пользователей.
Для начала о группах - служат они, как известно, для упрощения администрирования : вместо того, чтобы устанавливать права доступа, привилегии и т.д. каждому пользователю, их (пользователей) можно объединить в группы и настраивать права уже на уровне групп. Тем более, что для системы установка прав, как для групп, так и для пользователей практически одинакова.
Как известно, группы бываут локальные ("работают" только на одном (локальном) компьютере) и глобальные (для всего домена, если такой имеется). С точки зрения API работа с ними отличается. И хотя функции совпадают практически до параметров, но в примеры я буду давать для обоих типов.
Для работы с локальными группами используются функции NetLocalGroup*, а с глобальными NetGroup*.



Статья
Учетные записи групп пользователей


Начнем с перечисления групп. Пример будет даваться для обоих типов групп сразу, поэтому следите за комментариями :)
type
 // Локальные группы
 TLocalGroups = array of LOCALGROUP_INFO_1;
 // Глобальные
 TGroups = array of GROUP_INFO_1;

var
 pGroups : Pointer;
 Res, i  : Cardinal;
 cbread,cbTotal : Cardinal;
begin
 ListBox1.Clear;
 // Опять - первая локальная, вторая глобальная
 Res:= NetLocalGroupEnum(lpwSrv,1,pGroups,Cardinal(-1),cbRead,cbTotal,nil);
 Res:= NetGroupEnum(lpwSrv,1,pGroups,Cardinal(-1),cbRead,cbTotal,nil);

 For i:=0 to cbRead-1 do
  // Вывод групп в ListBox (как и выше )
  ListBox1.Items.Add(TLocalGroups(pGroups)[i].lgrpi1_name);
  ListBox1.Items.Add(TGroups(pGroups)[i].grpi1_name);

 // Очищаем буфер - Каждый удачный вызов Net*GroupEnum должен
 // сопровождаться вызовом NetApiBufferFree
 NetApiBufferFree(pGroups);
end;
Все зависит от задачи, но , как вы понимаете из этих пар строк должна остаться только одна нужная. lpwSrv - Имя сервера (PWideChar). cbRead - кол-во прочитанных групп, а cbTotal - их общее кол-во. В данном случае они должны совпадать, т.к. макс. кол-во перечисляемых групп не ограничено (4 параметром используется Cardinal(-1) ). Не ограничено оно в этом примере по двум причинам - размер рассылки (если вам очень интересно как перечислять группы "по частям", загляните в прошлый номер рассылки - там был подобный пример, но для перечисления пользователей). Второй причиной является то, что встретить сервер с несколькими тысячами групп практически невозможно (а вот с пользователями запросто).
И еще - проверяйте возвращаемое функцией значение. В случае удачи оно должно быть 0 и только 0. Теперь создадим группу - локальную и глобальную.
var
 lgi1 : _LOCALGROUP_INFO_1;
  Res : Cardinal;
begin
 lgi1.lgrpi1_name:=lpwGrpName; //Имя группы
 lgi1.lgrpi1_comment:='Group Comment'; // Комментарий
 Res:= NetLocalGroupAdd(lpwSrv,1,@lgi1,nil);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;

var
 gi : GROUP_INFO_1;
 Res : Cardinal;
begin
 gi.grpi1_name:=lpwGrpName;
 gi.grpi1_comment:='Commentariy';
 Res:= NetGroupAdd(lpwSrv,1,@gi,nil);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;
Удалять группы еще проще -
var
 Res : Cardinal;
begin
 // локальную
 Res:= NetLocalGroupDel(lpwSrv,lpwGrpName);
 // или глобальную
 Res:= NetGroupDel(lpwSrv,lpwGrpName);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;
Следующая пара функций - получение информации о группе. Для глобальных групп поддерживается 4 уровня. Уровень 0 дает нам только имя группы (интересно, зачем оно нам :), 2ой - кроме имени и комментария не дает ничего полезного - атрибуты жестко зафиксированы, а id как-то не очень нужен (вот если бы SID группы..., но он доступен только на 3 уровне, поддерживаемом только XPшкой). Поэтому как обычно будем пользоваться 1 уровнем.
// Получаем комментарий для локальной группы lpwGrpName на компьютере lpwSrv
var
 Buf : ^LOCALGROUP_INFO_1;
begin
 if NetLocalGroupGetInfo(lpwSrv,lpwGrpName,1,Pointer(Buf))=0
  then begin
   ShowMessage(Buf.lgrpi1_comment);
   NetApiBufferFree(Buf);
  end;
end;

// И то же самое для глобальной
var
 Buf : ^GROUP_INFO_1;
begin
 if NetGroupGetInfo(lpwSrv,lpwGrpName,1,Pointer(Buf))=0
  then begin
   ShowMessage(Buf.grpi1_comment);
   NetApiBufferFree(Buf);
  end;
end;
Ну и последнее - установка свойств групп, а именно комментария. Остальные (имя группы) параметры LOCALGROUP_INFO_1 и GROUP_INFO_1 игнорируются.
var
 LGI : LOCALGROUP_INFO_1;
 Res : Cardinal;
begin
 // новый коментарий локальной группы
 LGI.lgrpi1_comment:='NewComment';
 Res:=NetLocalGroupSetInfo(lpwSrv,lpwGrpName,1,@LGI,nil);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;

var
 GI : GROUP_INFO_1;
 Res : Cardinal;
begin
 // новый коментарий глобальной группы
 GI.grpi1_comment:='NewComment';
 Res:=NetGroupSetInfo(lpwSrv,lpwGrpName,1,@GI,nil);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;
О работе с группами это все. Теперь немного о пользователях и группах - перемещении, удалении, добавлении в группы.
// Добавляем в глобальную группу lpwGrpName юзера lpwUser
var
 Res : Cardinal;
begin
 Res:=NetGroupAddUser(nil,lpwGrpName,lpwUser);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;

// Удаляем из глобальной группы lpwGrpName юзера lpwUser
var
 Res : Cardinal;
begin
 Res:=NetGroupDelUser(nil,lpwGrpName,lpwUser);
 if Res<>ERROR_SUCCESS then ShowMessage(SysErrorMessage(Res));
end;
Продолжение (а точнее окончание) будет в следующем выпуске.


Вопросы и ответы
Как получить список привилегий, доступных программе (пользователю) ?

procedure ShowEnabledPrivileges;
const
 TokenSize=800; // размер выделяемой памяти (SizeOf(Pointer)=4 *200)

var
 hToken:THandle;
 pTokenInfo:PTOKENPRIVILEGES;

 ReturnLen:Cardinal;
 i:Integer;

 PrivName:PChar;
 DisplayName:PChar;
 NameSize:Cardinal;
 DisplSize:Cardinal;
 LangId:Cardinal;
begin
 // Выделяем память под массив
 GetMem(pTokenInfo,TokenSize);

 // Получаем токен нашего процесса
 if not OpenProcessToken(GetCurrentProcess(),
         TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
  then ShowMessage('OpenProcessToken error');
   // Получаем информацию о привилегиях процесса
   if not GetTokenInformation(hToken,TokenPrivileges,
            pTokenInfo,TokenSize,ReturnLen)
    then ShowMessage('GetTokenInformation error');
     GetMem(PrivName,255);
     GetMem(DisplayName,255);
     for i:=0 to pTokenInfo.PrivilegeCount-1 do
      begin
       DisplSize:=255;
       NameSize:=255;

      // получаем название и имя каждой из PrivilegeCount-1 привилегий
      LookupPrivilegeName(nil,pTokenInfo.Privileges[i].Luid,PrivName,Namesize);
      LookupPrivilegeDisplayName(nil,PrivName,DisplayName,DisplSize,LangId);

     // выводим их в ListBox1 типа TListBox (свойства: Columns>=2; TabWidth>0)
     ListBox1.Items.Add(PrivName+^I+DisplayName);
   end;
 FreeMem(PrivName);
 FreeMem(DisplayName);
 FreeMem(pTokenInfo);
end;


Напоследок
Прячем файлы с настройками

Предположим у вашей программы есть файл с настройками, в который желательно разрешить писать только вашей программе. Допустим, при запуске программы вы читаете настройки из этого файла, и при закрытии записываете новые. Но как сделать, чтобы доступ к настройкам был только у вашей программы и пользователи не правили его вручную?

Прежде чем описать его скажу, что он несколько экзотичен, работоспособен только на текущих версиях НТ (что ждать от следующего сервис-пака не известно), проверялся мною (а ссылку на оригинал информации я к сожалению утратил) на NTFS (а скорее всего только там и работает) и вероятно не защитит он advanced users с различными FileMon'ами.

Итак, что будет если мы создадим каталог, полный путь к которому будет больше, чем MAX_PATH = 260 символов? А будет то, что мы не получим доступа к его содержимому. Но сможем создать такой каталог обычным путем. Попробуем обходным...

Создаем каталог,полный путь которого (C:\test\много символов\) будет около 260 символов. Теперь пытаемся создать в этом каталоге подкаталог, полный путь которого (C:\test\много символов\catalog)будет больше 260 ... Вероятно не получилось. А если сделать subst, т.е. сопоставить нашему C:\test\много символов\ букву диска. А теперь создать в нем каталог h:\catalog = C:\test\много символов\catalog и записать в нем наш файлик.
Теперь доступ к файлу возможен только через "виртуальный" диск, а через реальный путь нет.
Теперь в начале и в конце работы программы назначаем нашему каталогу букву диска, читаем файлик и "отключаем" диск.

Выполнить сопоставление можно или программой subst или программно.


//За пример благодарим Ворническу Владимира

procedure addSubst(Drv: string; Path: string);
begin
  if (Length(Path) = 0) then Exit;
  if (DefineDosDevice(0, PChar(Drv), PChar(Path)) = False)
   then  RaiseLastWin32Error;
end;

procedure KillSubst(Drv: string);
begin
  if (DefineDosDevice(DDD_REMOVE_DEFINITION, PChar(Drv), nil) = False)
   then   RaiseLastWin32Error;
end;

addSubst('h:','c:\....\');
потом читаем наш файлик из подкаталога и
KillSubst('h:');

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

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



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

В избранное