1
votes

I'm having an issue where if I try extend the UserPrincipal class the PrincipalContextSearcher does not return the correct results when using the extended class as the query filter.

So, for example, If I create the following minimal extension of UserPrincipal

[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")]
public class UserPrincipalExtended : UserPrincipal
{
    public UserPrincipalExtended(PrincipalContext context) : base(context) { }
    public UserPrincipalExtended(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled) { }
}

If I search using a (non-extended) UserPrincipal, like follows:

    using (var searchCritera = new UserPrincipal(context))
    {
        searchCritera.SamAccountName = searchTerm;
        using (var searcher = new PrincipalSearcher(searchCritera))
        {
            foreach (var principal in searcher.FindAll())
            {
                ... do stuff
            }
        }
     }

It will correctly return only user accounts. But if I use UserPrincipalExtended instead of the UserPrincipal, it returns matches to computers and all kinds of other things which is not the behavior I want. All I want to do is be able to add a few additional properties to retrieve in the UserPrincipal but simply extending the class before adding anything into it seems to change the filtering behavior.

What am I missing and how do I get PrincipalSearcher using an extended UserPrincipal ?

2

2 Answers

1
votes

Digging a bit as to what was going on I looked at the DirectorySearcher used under the covers of PrincipalSearcher to see what the difference was.

((System.DirectoryServices.DirectorySearcher)(principalSearcher.GetUnderlyingSearcher())).Filter

when using UserPrincipal as the queryfilter the filter looks like:

    Filter: "(&(objectCategory=user)(objectClass=user)(sAMAccountName=*searchTermWithWildcards*))"  string

when using UserPrincipalExtended as the queryfilter the filter looks like:

    Filter:  "(&(objectClass=user)(sAMAccountName=*searchTermWithWildcards*))"    string

It's missing the objectCategory

I resorted to the following deplorable hack to get it working but I would prefer a better way to specify the objectCategory

[DirectoryRdnPrefix("CN")]
#region *UGLY CODE WARNING* Don't look in here....
// When building the underlying DirectorySearcher filter the DirectoryObjectClass value is inserted in as: (objectClass={value})
// The following injection will result in : (objectClass=user)(objectCategory=user)
[DirectoryObjectClass("user)(objectCategory=user")]
#endregion

public class UserPrincipalExtended : UserPrincipal
{
    public UserPrincipalExtended(PrincipalContext context) : base(context) { }
    public UserPrincipalExtended(PrincipalContext context, string samAccountName, string password, bool enabled)
        : base(context, samAccountName, password, enabled) { }
}
0
votes

It's possible to replace the AdvancedSearchFilter property (using new not override, because the original property is read-only) and add your very own CustomAdvancedFilter with any custom filter you want using AdvancedFilterSet.

[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")]
public class UserPrincipalExtended : UserPrincipal
{
    public UserPrincipalExtended(PrincipalContext context) : base(context) {
        AdvancedSearchFilter = new CustomAdvancedFilter(this);
    }
    public UserPrincipalExtended(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled)
    {
        AdvancedSearchFilter = new CustomAdvancedFilter(this);
    }

    public new AdvancedFilters AdvancedSearchFilter { get; set; }
}

public class CustomAdvancedFilter : AdvancedFilters
{
    public CustomAdvancedFilter(Principal principal) : base(principal)
    {
        AdvancedFilterSet("objectCategory", "user", typeof(string), MatchType.Equals);
    }
}