2
votes

I'm pretty new to active directory and I'm currently working on a library for a project to easily manage our active directory objects like users, resources, groups and so on.

The library is in .NetStandard 2.0 and I use the Principal classes from

System.DirectoryServices.AccountManagement

As the UserPrincipal class doesn't contain all the properties that we could need, I tried to implement an UserPrincipalExtended class that, for now, just add the Initials property.

Here is my class :

[DirectoryObjectClass("user")]
[DirectoryRdnPrefix("CN")]
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) { }

    [DirectoryProperty("Initials")]
    public string Initials
    {
        get
        {
            if (ExtensionGet("initials").Length != 1) return null;

            return (string)ExtensionGet("initials")[0];

        }
        set { ExtensionSet("initials", value); }
    }

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

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

When I do a search in the active directory using the UserPrincipal class, it works as expected :

using (var context = _contextProvider.GetPrincipalContext())
using (var query = new UserPrincipal(context))
using (var searcher = new PrincipalSearcher(query))
{
    foreach (var principal in searcher.FindAll())
    {
        UserPrincipal userPrincipal = principal as UserPrincipal;

        if (CheckHelper.IsFilled(userPrincipal))
        {
            Console.WriteLine($"{userPrincipal.StructuralObjectClass} : {userPrincipal.SamAccountName}");
        }
    }
}

/*Output
user : cadmin
user : Guest
user : DefaultAccount
*/

But if I try to perform the same search using my own class, the result contains also computers :

using (var context = _contextProvider.GetPrincipalContext())
using (var query = new UserPrincipalExtended(context))
using (var searcher = new PrincipalSearcher(query))
{
    foreach (var principal in searcher.FindAll())
    {
        UserPrincipalExtended userPrincipalExtended = principal as UserPrincipalExtended;

        if (CheckHelper.IsFilled(userPrincipalExtended))
        {
            Console.WriteLine($"userPrincipalExtended.StructuralObjectClass} : {userPrincipalExtended.SamAccountName}");
        }
    }
}
/*Output
user : cadmin
user : Guest
user : DefaultAccount
computer : WS001$
computer : WS002$
computer : WS003$
*/

As my UserPrincipalExtended class has the attribute :

[DirectoryObjectClass("user")]

I thought that was enough to filter on this object type in the active directory, but it seems it does not.

Any idea of what's going on here?

Cheers

2
computer inherits from user in the standard AD directory schema. All computers "are" users.Damien_The_Unbeliever
But then, why they are not returned when I do a search using the UserPrinicpal ? Why does the behavior be different since I extend this class ?Papapinguy
Microsoft does not publish the relevant source code, referencesource.microsoft.com/#q=system.directoryservices so you have to dig that using a decompiler.Lex Li
In your class you are using UserPrincipalExtended. In the example search in the active directory you are using UserPrincipal. So, you are not trying to perform the same search as you stated.Theo

2 Answers

1
votes

In your constructor you can set the ObjectCategory attribute to User

[DirectoryObjectClass("user")]
[DirectoryRdnPrefix("CN")]
public class UserPrincipalExtended : UserPrincipal
{
    public UserPrincipalExtended(PrincipalContext context) : base(context)
    {
        // Set ObjectCategory to user so computer objects are not returned
        ExtensionSet("objectCategory", "user");
    }

    [DirectoryProperty("Initials")]
    public string Initials
    {
        get
        {
            if (ExtensionGet("initials").Length != 1) return null;
            return (string)ExtensionGet("initials")[0];
        }
        set { ExtensionSet("initials", value); }
    }
}
1
votes

microsoft Principal types filter creating code

Also faced with this problem.Having rummaged in the source code, I found such a workaround.

[DirectoryObjectClass("user)(objectCategory=user")]