Как я могу преобразовать из SID в имя учетной записи в C #

У меня есть приложение C #, которое сканирует каталог и собирает некоторую информацию. Я хотел бы отображать имя учетной записи для каждого файла. Я могу сделать это в локальной системе, получив SID для объекта FileInfo, а затем выполнив:

string GetNameFromSID( SecurityIdentifier sid )
{
    NTAccount ntAccount = (NTAccount)sid.Translate( typeof( NTAccount ) );
    return ntAccount.ToString();
}

Однако это не работает для файлов в сети, предположительно потому, что функция Translate () работает только с локальными учетными записями пользователей. Я подумал, может быть, я смогу выполнить поиск LDAP по SID, поэтому я попробовал следующее:

string GetNameFromSID( SecurityIdentifier sid )
{
    string str = "LDAP://<SID=" + sid.Value + ">";
    DirectoryEntry dirEntry = new DirectoryEntry( str );
    return dirEntry.Name;
}

Похоже, это сработает, поскольку доступ к "dirEntry.Name" зависает на несколько секунд, как будто он уходит и запрашивает сеть, но затем выдает исключение System.Runtime.InteropServices.COMException

Кто-нибудь знает, как я могу получить имя учетной записи произвольного файла или SID? Я не очень разбираюсь в сетях, LDAP и т. Д. Есть класс под названием DirectorySearcher, который, возможно, я должен использовать, но ему нужно доменное имя, и я тоже не знаю, как его получить - все, что у меня есть, - это путь к каталогу, который я сканирую.

Заранее спасибо.


person Community    schedule 31.01.2009    source источник
comment
Есть ли конкретное сообщение, которое показывает COMException?   -  person David Brown    schedule 31.01.2009
comment
Это может быть групповая политика, позволяющая вам перемещаться по службе каталогов локально, поэтому вы получаете исключение COM. Что показывает сообщение об ошибке? Попробуйте запустить filemon на сетевом компьютере, почему вы к нему обращаетесь, и посмотрите результаты.   -  person Saif Khan    schedule 31.01.2009
comment
Я имел в виду групповую политику, не разрешающую ...   -  person Saif Khan    schedule 31.01.2009


Ответы (10)


Метод Translate объекта SecurityReference работает с нелокальными идентификаторами безопасности, но только для учетных записей домена. Для учетных записей, локальных для другого компьютера или в настройке, не относящейся к домену, вам потребуется PInvoke функции LookupAccountSid, указав конкретное имя компьютера, на котором необходимо выполнить поиск.

person Stephen Martin    schedule 01.02.2009
comment
Я только начал писать код, чтобы сделать что-то подобное, и нашел, что это правильно. P / Вызов LookupAccountSid лучше, чем использование SecurityIdentifier.Translate (). Я работаю в среде с несколькими лесами / доменами с более чем 60 тысячами пользователей. - person Brian Lyttle; 03.12.2009
comment
Translatemethod возвращает имена групп до Windows 2000. Вместо PInvoke вы также можете использовать FindByIdentity метод oonSystem.DirectoryServices.AccountManagement.GroupPrincipal. - person Daniel Fisher lennybacon; 10.07.2019

См. Здесь хороший ответ:

Как лучше всего разрешить отображаемое имя пользователя по SID?

Суть в следующем:

string sid="S-1-5-21-789336058-507921405-854245398-9938";
string account = new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

Этот подход работает для меня для нелокальных SID в активном каталоге.

person Chris    schedule 11.03.2009

System.DirectoryServices.AccountManagement.UserPrincipal класс (ссылка msdn) имеет статическую функцию FindByIdentity для преобразования SID в Пользовательский объект. Он должен работать как с локальным компьютером, так и с сервером LDAP / Active Directory. Я использовал его только против активного каталога.

Вот пример, который я использовал в IIS:

// Set the search context to a specific domain in active directory
var searchContext = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "OU=SomeOU,DC=YourCompany,DC=com");
// get the currently logged in user from IIS
MembershipUser aspUser = Membership.GetUser();
// get the SID of the user (stored in the SecurityIdentifier class)
var sid = aspUser.ProviderUserKey as System.Security.Principal.SecurityIdentifier;
// get the ActiveDirectory user object using the SID (sid.Value returns the SID in string form)
var adUser = UserPrincipal.FindByIdentity(searchContext, IdentityType.Sid, sid.Value);
// do stuff to user, look up group membership, etc.
person Bakanekobrain    schedule 19.07.2012
comment
Это правильный способ запроса чужих доменов. P / Invoke не требуется. - person Tamir Daniely; 08.01.2015

В C # получите SID пользователя и назначьте его строковой переменной с помощью:

string strUser = System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString();

Вам нужно будет использовать строку, потому что возможность преобразования в UserName поддерживает строку. Другими словами, использование var varUser приведет к ошибке пространства имен.

string strUserName = new System.Security.Principal.SecurityIdentifier(strUser).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
person J Weezy    schedule 19.02.2017
comment
Это вызвало проблему = не удалось установить доверительные отношения между этой рабочей станцией и основным доменом. - person user1034912; 08.07.2020
comment
@ user1034912 Какие действия по устранению неполадок вы выполняли? Вы запускали VS в режиме администратора? Вы проверяли MSDN на наличие этой ошибки: support.microsoft.com/en-us/help/2771040/? - person J Weezy; 08.07.2020
comment
Забудьте об этом, я решил эту проблему, повторно присоединившись к домену .. мучительно ... но это сработало! - person user1034912; 08.07.2020

Вы также можете получить имя учетной записи для специальных учетных записей, таких как «Все», с помощью такого кода, который будет работать независимо от языковых настроек пользователя:

   SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
   string everyone = everyoneSid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();
person jm.    schedule 01.12.2015
comment
Ткс. Это спасло меня :) - person Regis St-Gelais; 17.02.2021

Ооо, тогда возможно, что вызов LDAP не работает, потому что вы не находитесь в среде Active Directory. В этом случае каждая из ваших машин отвечает за собственное хранилище идентификационных данных. И ваш первый образец кода не работает по сети, потому что машина, на которой вы выполняете свой код, не знает, как разрешить SID, который имеет смысл только на удаленной машине.

Вам действительно следует проверить, являются ли ваши машины частью Active Directory. Вы узнаете об этом во время входа в систему. Или вы можете проверить, щелкнув правой кнопкой мыши «Мой компьютер», выберите «Свойства», вкладку «Имя компьютера», а затем посмотрите, является ли ваш компьютер частью домена.

person barneytron    schedule 01.02.2009

Здорово. Я написал здесь код LookupAccountSid ():

http://www.pinvoke.net/default.aspx/advapi32.LookupAccountSid

И это сработало, хотя мне пришлось самому указать имя хоста. В случае пути UNC я могу просто взять его первый компонент. Когда это подключенный диск, я использую этот код для преобразования пути в UNC:

http://www.wiredprairie.us/blog/index.php/archives/22

Кажется, это работает, так что я сделаю это, если только кто-то не придумает ситуацию, в которой первый компонент пути UNC не является именем хоста ...

Спасибо за вашу помощь.

person Community    schedule 02.02.2009

Это загвоздка. Вы ведь работаете в среде Active Directory? Просто проверка:)

Во всяком случае, вместо привязки к sid.Value,

string str = "LDAP://<SID=" + sid.Value + ">";

Я бы попытался преобразовать байтовый массив SID в Octet String и вместо этого привяжите его.

Вот замечательный пример здесь на стр. 78. Это приблизит вас. Если честно, привязку с SID раньше не пробовал. Но у меня была успешная привязка с GUID пользователя :)

Удачи и дайте мне знать, как дела.

person barneytron    schedule 31.01.2009

Получите текущий домен:

System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();

Получите запись каталога от ldap и имя домена:

DirectoryEntry de = new DirectoryEntry(string.Format("LDAP://{0}", domain));

Получите sid от ActiveDirectoryMembershipProvider ActiveDirectoryMembershipUser:

ActiveDirectoryMembershipUser user = (ActiveDirectoryMembershipUser)Membership.GetUser();
var sid = (SecurityIdentifier)user.ProviderUserKey;

Получите имя пользователя из SecurityIdentifier:

(NTAccount)sid.Translate(typeof(NTAccount));

Выполните поиск в каталоге activedirectory с записью в каталоге домена и именем пользователя:

DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = string.Format("(SAMAccountName={0})", username);
        search.PropertiesToLoad.Add("Name");
        search.PropertiesToLoad.Add("displayName");
        search.PropertiesToLoad.Add("company");
        search.PropertiesToLoad.Add("homePhone");
        search.PropertiesToLoad.Add("mail");
        search.PropertiesToLoad.Add("givenName");
        search.PropertiesToLoad.Add("lastLogon");
        search.PropertiesToLoad.Add("userPrincipalName");
        search.PropertiesToLoad.Add("st");
        search.PropertiesToLoad.Add("sn");
        search.PropertiesToLoad.Add("telephoneNumber");
        search.PropertiesToLoad.Add("postalCode");
        SearchResult result = search.FindOne();
        if (result != null)
        {
            foreach (string key in result.Properties.PropertyNames)
            {
                // Each property contains a collection of its own
                // that may contain multiple values
                foreach (Object propValue in result.Properties[key])
                {
                    outputString += key + " = " + propValue + ".<br/>";
                }
            }
        }

В зависимости от данных в вашем активном каталоге вы получите различный ответ на выходе.

Вот сайт, на котором есть все необходимые мне свойства пользователя:

person Community    schedule 17.05.2009

Я совершенно уверен, что вы сможете использовать принятый ответ отсюда: Определить Имя учетной записи LocalSystem с использованием C #

По сути, вы можете преобразовать экземпляр класса SecurityIdentifier в тип NTAccount, из которого вы можете получить имя пользователя. В коде:

using System.Security.Principal;

SecurityIdentifier sid = new SecurityIdentifier("S-1-5-18");
NTAccount acct = (NTAccount)sid.Translate(typeof(NTAccount));
Console.WriteLine(acct.Value);
person driis    schedule 01.02.2009
comment
Хотя это не неправильно, он просто повторяет код, который уже указал автор, не работает в его ситуации. - person Stephen Martin; 02.02.2009