Поиск идентификаторов пользователя 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. У меня нет кода под рукой, но если он вам нужен, я всегда могу его найти.

Другие вопросы по тегам