Как я могу преобразовать из 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, который, возможно, я должен использовать, но ему нужно доменное имя, и я не знаю, как его получить - все, что у меня есть, - это путь к каталогу, который я сканирую.

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

11 ответов

Решение

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

Смотрите здесь для хорошего ответа:

Лучший способ разрешить отображение имени пользователя по 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();

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

Класс System.DirectoryServices.AccountManagement.UserPrincipal ( ссылка msdn) имеет статическую функцию FindByIdentity для преобразования SID в объект User. Он должен работать как на локальном компьютере, так и на сервере 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.

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

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

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

string strUserName = new System.Security.Principal.SecurityIdentifier(strUser).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

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

   SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
   string everyone = everyoneSid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();

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

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

Отлично. Я набрал некоторый код LookupAccountSid() отсюда:

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

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

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

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

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

Для всех разработчиков Windows ответ: LookupAccountSid.

      LookupAccountSid(null, Sid, username, userSize, domainName, domainSize, sidType);

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

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));

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

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/>";
                }
            }
        }

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

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

Это тупик. Вы находитесь в среде Active Directory, верно? Просто проверяю:)

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

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

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

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

Удачи и дайте мне знать, как это происходит.

Я совершенно уверен, что вы сможете использовать принятый ответ отсюда: Определите имя учетной записи 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);
Другие вопросы по тегам