Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a username in Active Directory from a display name in C#?

I want to be able to obtain the userid of a user in Active Directory using the display name of that user. The display name is obtained from a database, and has been stored during that user's session using the following code to obtain the display name:

using System.DirectoryServices.AccountManagement;

    private string GetDisplayName()
    {
        // set up domain context
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

        // find currently logged in user
        UserPrincipal user = UserPrincipal.Current;

        return user.DisplayName;
    }

This time around, I would like to have a method named GetUserIdFromDisplayName() that returns the Active Directory login name. Any ideas?

like image 876
JF Beaulieu Avatar asked Mar 23 '12 19:03

JF Beaulieu


People also ask

Is Display name unique in Active Directory?

On a user object, display name is normally the user's first name followed by their last name, but it can be set to any string. The rules for display names are: - Local display names must be unique on a workstation. - Display names must be unique throughout a domain.

What is C# PrincipalContext?

PrincipalContext(ContextType, String, String, ContextOptions) Initializes a new instance of the PrincipalContext class with the specified context type, name, container, and context options.


2 Answers

I believe you can do it much more easily than with David's answer by using the built-in functionality of the System.DirectoryServices.AccountManagement (S.DS.AM) namespace.

Basically, you can define a domain context and easily find users and/or groups in AD:

using System.DirectoryServices.AccountManagement;

private string GetUserIdFromDisplayName(string displayName)
{
    // set up domain context
    using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
    {
        // find user by display name
        UserPrincipal user = UserPrincipal.FindByIdentity(ctx, displayName);

        // 
        if (user != null)
        {
             return user.SamAccountName;
             // or maybe you need user.UserPrincipalName;
        }
        else
        {
             return string.Empty;
        }
    }
}

I don't see any need to go to the underlying DirectoryEntry object, really - unless none of the properties of the UserPrincipal really are what you're looking for.

PS: if the search by display name shouldn't work (I don't have an AD at hand to test it right now) - you can always also use the PrincipalSearcher to find your user:

using System.DirectoryServices.AccountManagement;

private string GetUserIdFromDisplayName(string displayName)
{
    // set up domain context
    using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
    {
        // define a "query-by-example" principal - here, we search for a UserPrincipal 
        // and with the display name passed in
        UserPrincipal qbeUser = new UserPrincipal(ctx);
        qbeUser.DisplayName = displayName;

        // create your principal searcher passing in the QBE principal    
        PrincipalSearcher srch = new PrincipalSearcher(qbeUser);

        // find match - if exists
        UserPrincipal user = srch.FindOne() as UserPrincipal;

        if (user != null)
        {
             return user.SamAccountName;
             // or maybe you need user.UserPrincipalName;
        }
        else
        {
             return string.Empty;
        }
    }
}
like image 151
marc_s Avatar answered Sep 24 '22 07:09

marc_s


UserPrincipal has a method GetUnderlyingObject() which will return the DirectoryEntry.

Get DirectoryEntry from Principal:

private DirectoryEntry GetDirectoryEntryFromUserPrincipal(Principal user)
{
    return (DirectoryEntry)user.GetUnderlyingObject();
}

Get DirectoryEntry from domain and account name:

private DirectoryEntry GetDirectoryEntryFromDomainAndUsername(string domainName, string userName)
{
    // Get the sid from the NT account name
    var sid = (SecurityIdentifier) new NTAccount(domainName, accountName)
                  .Translate(typeof(SecurityIdentifier));

    // Get the directory entry for the LDAP service account
    var serviceEntry = new DirectoryEntry("LDAP://{address}", "serviceUsername", "servicePassword");

    var mySearcher = new DirectorySearcher(serviceEntry)
        {
            Filter = string.Format("(&(ObjectSid={0}))", sid.Value)
        };

    return mySearcher.FindOne().GetDirectoryEntry();
}

Once you have the DirectoryEntry use the Guid property to get the entry's Object-Guid

private Guid GetObjectGuidFromDirectoryEntry(DirectoryEntry entry)
{
    // return the Guid this is the Object-Guid (ignore NativeGuid)
    return entry.Guid;
}

For tracking a user account in the application against a directory account: always use the Object-Guid as "This value is set when the object is created and cannot be changed."
NT and SAM account names can change if the user changes domains or, more commonly, changes their name (marriage, legal name-change, etc.) and should not be used to track a user.

To get the NT account name (domain\username):

private string GetNTAccountNameFromDirectoryEntry(DirectoryEntry entry)
{
    PropertyValueCollection propertyValueCollection = entry.Properties["objectsid"];

    SecurityIdentifier sid = new SecurityIdentifier((byte[]) propertyValueCollection[0], 0);

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

    return account.ToString();
}

To get the SAM-Account-Name (username@domain):

private string GetSAMAccountFromDirectoryEntry(DirectoryEntry entry)
{
    return entry.Properties["Name"].Value;
}

And here's the exhaustive list of all the Active Directory attributes. Use the "Ldap-Display-Name" when getting the value from Properties
e.g. Properties["Ldap-Display-Name"]

Display-Name (FirstName MI LastName) might come in handy.

like image 45
David Avatar answered Sep 20 '22 07:09

David