Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get a custom field from Active Directory in C#?

I've read plenty of similar StackOverflow questions but none seem to address the issue I'm seeing. If I query for a user using userprincipalname I get back a search result with exactly 34 properties. None of the custom properties are returned. If I query again using a custom property like employeeNumber I get back a result with 71 properties. All custom properties are included.

My issue is that I don't have the employeeNumber at run time, just the userprincipalname. I need to get back all of the custom properties all of the time. Hopefully that makes sense. Here's my practice code:

string sid = "";
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
    UserPrincipal user = UserPrincipal.Current;
    //sid = user.SamAccountName;
    sid = user.UserPrincipalName;
    //sid = user.Sid.ToString();

    DirectoryEntry entry = user.GetUnderlyingObject() as DirectoryEntry;
    if (entry.Properties.Contains("employeeNumber"))
    {
        //this doesn't work
    }
}

DirectoryEntry ldapConnection = new DirectoryEntry("companyname.com");
ldapConnection.Path = "LDAP://DC=companyname,DC=com";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;

DirectorySearcher search = new DirectorySearcher(ldapConnection);
search.Filter = string.Format("(&(ObjectClass=user)(userprincipalname={0}))", sid); // <-- this doesn't get custom properties
//search.Filter = string.Format("(employeeNumber={0})", "11663"); <-- this works

var result = search.FindOne(); // FindOne();
if (result.Properties.Contains("employeeNumber"))
{
    //this never happens either :(
}

The above never returns the employeeNumber field, but if I uncomment the second search.Filter line and manually search by employeeNumber I find a result and it contains all of the fields that I need.

EDIT: I found a very good MSDN article Here which describes how to extend the UserPrincipal object to get custom attributes. The only issue is that it's giving me an empty string every time I access it even though I have verified that the property IS set in AD! Any help is appreciated.

EDIT 2: Here's the code for the custom principal extension:

[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("Person")]
public class UserPrincipalExtension : UserPrincipal
{
    public UserPrincipalExtension(PrincipalContext context)
        : base(context)
    {
    }

    public UserPrincipalExtension(PrincipalContext context, string samAccountName, string password, bool enabled)
        : base(context, samAccountName, password, enabled)
    {
    }

    public static new UserPrincipalExtension FindByIdentity(PrincipalContext context, IdentityType type, string identityValue)
    {
        return (UserPrincipalExtension)FindByIdentityWithType(context, typeof(UserPrincipalExtension), type, identityValue);
    }

    PersonSearchFilter searchFilter;
    new public PersonSearchFilter AdvancedSearchFilter
    {
        get
        {
            if (searchFilter == null)
                searchFilter = new PersonSearchFilter(this);

            return searchFilter;
        }
    }

    [DirectoryProperty("employeeNumber")]
    public string EmployeeNumber
    {
        get
        {
            if (ExtensionGet("employeeNumber").Length != 1)
                return string.Empty;

            return (string)ExtensionGet("employeeNumber")[0];
        }
        set
        {
            ExtensionSet("employeeNumber", value);
        }
    }
}

And the custom search filter:

public class PersonSearchFilter : AdvancedFilters
{
    public PersonSearchFilter(Principal p)
        : base(p)
    {
    }

    public void SAMAccountName(string value, MatchType type)
    {
        this.AdvancedFilterSet("sAMAccountName", value, typeof(string), type);
    }
}

Usage:

UserPrincipalExtension filter = new UserPrincipalExtension(context);
filter.AdvancedSearchFilter.SAMAccountName(UserPrincipal.Current.SamAccountName, MatchType.Equals);
PrincipalSearcher search = new PrincipalSearcher(filter);

foreach (var result in search.FindAll())
{
    var q = (UserPrincipalExtension)result;
    var m = q.EmployeeNumber;
}

var m is always an empty string even though ALL AD entries have an employeeNumber.

EDIT: From active directory: Active Directory

like image 536
BrianLegg Avatar asked Jul 01 '15 15:07

BrianLegg


1 Answers

I am more confidant in how to fix your second method so I will answer that first. You need to specify the property in DirectorySearcher.PropertiesToLoad when you do your search for the property to show up.

DirectorySearcher search = new DirectorySearcher(ldapConnection);
search.Filter = string.Format("(&(ObjectClass=user)(userprincipalname={0}))", sid); 
search.PropertiesToLoad.Add("employeeNumber");

var result = search.FindOne(); // FindOne();
if (result.Properties.Contains("employeeNumber"))
{
    //This should now work.
}

The reason it worked for string.Format("(employeeNumber={0})", "11663"); is because any search clause you add automatically gets put in to the PropertiesToLoad collection.


For your first method, I think you need to call DirectoryEntry.RefreshCache and pass in the property to make it show up.

string sid = "";
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
    UserPrincipal user = UserPrincipal.Current;
    //sid = user.SamAccountName;
    sid = user.UserPrincipalName;
    //sid = user.Sid.ToString();

    DirectoryEntry entry = user.GetUnderlyingObject() as DirectoryEntry;

    entry.RefreshCache(new[] {"employeeNumber"});

    if (entry.Properties.Contains("employeeNumber"))
    {

    }
}

But I am not 100% sure if that will work or not.


For your custom user principal, I am not sure what is wrong. The only difference I see between what you are doing and what I have in a project that does it that I know works is you use [DirectoryObjectClass("Person")] but I have [DirectoryObjectClass("user")]. Perhaps that is the issue

[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")] //Maybe this will fix it???
public class UserPrincipalExtension : UserPrincipal
{
like image 85
Scott Chamberlain Avatar answered Oct 05 '22 02:10

Scott Chamberlain