Поиск идентификаторов пользователя Windows в C#
контекст
Контекст первый - проблемы, которые я пытаюсь решить, приведены ниже.
[РЕДАКТИРОВАТЬ] Приложение в вопросах построен на.NET 3.5 SP1.
Один из наших клиентов задал вопрос о том, сколько времени нам потребуется, чтобы улучшить одно из наших приложений. Это приложение в настоящее время обеспечивает базовую аутентификацию пользователя в виде комбинаций имени пользователя и пароля. Этот клиент хотел бы, чтобы его сотрудники могли входить в систему, используя информацию о том, какая учетная запись пользователя Windows в данный момент вошла в систему во время запуска приложения.
Если я скажу им "нет", это не нарушит условия соглашения, но клиент может быть готов оплатить расходы на разработку, чтобы добавить эту функцию в приложение. Это стоит посмотреть.
Исходя из моей охоты, кажется, что сохранение данных для входа пользователя в систему по домену \ имени пользователя будет проблематичным, если эти данные будут изменены. Но SID пользователей Windows не должны меняться вообще. У меня сложилось впечатление, что было бы лучше записывать пользователей Windows по SID - не стесняйтесь, если вы ошибаетесь, избавьте меня от этого.
У меня была скрипка с некоторыми вызовами Windows API. Из C# захват SID текущего пользователя достаточно прост. Я уже могу взять SID любого пользователя и обработать его, используя LookupAccountSid, чтобы получить имя пользователя и домен для отображения. Для заинтересованных, мой код для этого в конце этого поста.
Это только верхушка айсберга, однако. Две проблемы ниже полностью вне моего опыта. Я не только не знаю, как их реализовать - я даже не знаю, как узнать, как их реализовать, или каковы подводные камни в различных системах.
Любая помощь, направленная в правильном направлении, будет очень признательна.
Выпуск 1)
Завладеть локальным пользователем во время выполнения не имеет смысла, если этому пользователю не был предоставлен доступ к приложению. Нам нужно будет добавить новый раздел в "консоль администратора" нашего приложения для добавления пользователей (или групп) Windows и назначения разрешений внутри приложения для этих пользователей.
Что-то вроде кнопки "Добавить учетную запись пользователя Windows", которая откроет всплывающее окно, которое позволит пользователю искать доступные учетные записи пользователей Windows в сети (не только на локальном компьютере), которые будут добавлены в список доступных входов в систему приложения.,
Если в.NET или Windows уже есть компонент, который я могу сделать для меня в Шанхае, это сделало бы меня очень счастливым человеком.
Выпуск 2)
Я также хочу знать, как взять заданный SID пользователя Windows и сравнить его с заданной группой пользователей Windows (вероятно, взятой из базы данных). Я не уверен, как начать с этим также, хотя я ожидаю, что это будет легче чем проблема выше.
Для заинтересованных
[STAThread]
static void Main(string[] args)
{
MessageBox.Show(WindowsUserManager.GetAccountNameFromSID(WindowsIdentity.GetCurrent().User.Value));
MessageBox.Show(WindowsUserManager.GetAccountNameFromSID("S-1-5-21-57989841-842925246-1957994488-1003"));
}
public static class WindowsUserManager
{
public static string GetAccountNameFromSID(string SID)
{
try
{
StringBuilder name = new StringBuilder();
uint cchName = (uint)name.Capacity;
StringBuilder referencedDomainName = new StringBuilder();
uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
WindowsUserManager.SID_NAME_USE sidUse;
int err = (int)ESystemError.ERROR_SUCCESS;
if (!WindowsUserManager.LookupAccountSid(null, SID, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
{
err = Marshal.GetLastWin32Error();
if (err == (int)ESystemError.ERROR_INSUFFICIENT_BUFFER)
{
name.EnsureCapacity((int)cchName);
referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);
err = WindowsUserManager.LookupAccountSid(null, SID, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse) ?
(int)ESystemError.ERROR_SUCCESS :
Marshal.GetLastWin32Error();
}
}
if (err != (int)ESystemError.ERROR_SUCCESS)
throw new ApplicationException(String.Format("Could not retrieve acount name from SID. {0}", SystemExceptionManager.GetDescription(err)));
return String.Format(@"{0}\{1}", referencedDomainName.ToString(), name.ToString());
}
catch (Exception ex)
{
if (ex is ApplicationException)
throw ex;
throw new ApplicationException("Could not retrieve acount name from SID", ex);
}
}
private enum SID_NAME_USE
{
SidTypeUser = 1,
SidTypeGroup,
SidTypeDomain,
SidTypeAlias,
SidTypeWellKnownGroup,
SidTypeDeletedAccount,
SidTypeInvalid,
SidTypeUnknown,
SidTypeComputer
}
[DllImport("advapi32.dll", EntryPoint = "GetLengthSid", CharSet = CharSet.Auto)]
private static extern int GetLengthSid(IntPtr pSID);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool ConvertStringSidToSid(
string StringSid,
out IntPtr ptrSid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool LookupAccountSid(
string lpSystemName,
[MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse);
private static bool LookupAccountSid(
string lpSystemName,
string stringSid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse)
{
byte[] SID = null;
IntPtr SID_ptr = IntPtr.Zero;
try
{
WindowsUserManager.ConvertStringSidToSid(stringSid, out SID_ptr);
int err = SID_ptr == IntPtr.Zero ? Marshal.GetLastWin32Error() : (int)ESystemError.ERROR_SUCCESS;
if (SID_ptr == IntPtr.Zero ||
err != (int)ESystemError.ERROR_SUCCESS)
throw new ApplicationException(String.Format("'{0}' could not be converted to a SID byte array. {1}", stringSid, SystemExceptionManager.GetDescription(err)));
int size = (int)GetLengthSid(SID_ptr);
SID = new byte[size];
Marshal.Copy(SID_ptr, SID, 0, size);
}
catch (Exception ex)
{
if (ex is ApplicationException)
throw ex;
throw new ApplicationException(String.Format("'{0}' could not be converted to a SID byte array. {1}.", stringSid, ex.Message), ex);
}
finally
{
// Always want to release the SID_ptr (if it exists) to avoid memory leaks.
if (SID_ptr != IntPtr.Zero)
Marshal.FreeHGlobal(SID_ptr);
}
return WindowsUserManager.LookupAccountSid(lpSystemName, SID, lpName, ref cchName, ReferencedDomainName, ref cchReferencedDomainName, out peUse);
}
}
1 ответ
Если вы используете версию 3.5 платформы, вы действительно хотите заглянуть в System.DirectoryServices.AccountManagement. Я использовал его раньше для поиска учетных записей AD, и с ним гораздо проще иметь дело, чем с написанием собственного класса. Это также решит ваш вопрос №2. У меня нет кода под рукой, но если он вам нужен, я всегда могу его найти.