Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find a UserPrincipal where a property is not set using a PrincipalSearcher?

I'm trying to search for users in an instance of AD LDS (ADAM) where a property is not set, for example where the "company" property is not set to a value in the ADAM store (or AD for that matter).

When I use a PrincipalSearcher and a custom UserPrincipal with a custom AdvancedSearchFilters object, I get the error:

An unhandled exception of type 'System.ArgumentException' occurred in System.DirectoryServices.dll

Additional information: The (&(objectClass=user)(!(company=))) search filter is invalid.

Here is my sample code:

using System;
using System.DirectoryServices.AccountManagement;
using System.Security.Permissions;
using System.Linq;

namespace AdamDump
{
    class Program
    {
        static void Main(string[] args)
        {
            PrincipalContext context = new PrincipalContext(ContextType.ApplicationDirectory, "MyAdamInstance:50000", "OU=Adam Users,dc=apps01,dc=mydomain", "queryaccount", "password");        

            // initialize a Query By Example
            using (MyUserPrincipal myUserPrincipal = new MyUserPrincipal(context))
            {
                myUserPrincipal.MyAdvancedFilters.WhereCompanyNotSet();

                PrincipalSearchResult<Principal> principals = null;

                // do the search...
                using (PrincipalSearcher principalSearcher = new PrincipalSearcher(myUserPrincipal))
                {
                    principals = principalSearcher.FindAll();
                }

                var myUsers = principals.Select(principal => principal as MyUserPrincipal).ToList();

                foreach (var user in myUsers)
                    Console.WriteLine("Name: {0}, Account{1}", user.DisplayName, user.SamAccountName);

                Console.WriteLine("Total found: {0}", myUsers.Count);
            }                 
        }
    }


    [DirectoryObjectClass("user")]
    [DirectoryRdnPrefix("CN")]
    [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
    public class MyUserPrincipal : UserPrincipal
    {
        private MyAdvancedFilters _myAdvancedFilters;

        /// <summary>
        /// Initializes a new instance of the <see cref="MyUserPrincipal"/> class.
        /// </summary>
        /// <param name="context">A <see cref="PrincipalContext"/> to associate this instance with.</param>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
        public MyUserPrincipal(PrincipalContext context)
            : base(context) { }

        public MyAdvancedFilters MyAdvancedFilters
        {
            get
            {
                return this.AdvancedSearchFilter as MyAdvancedFilters;
            }
        }

        public override AdvancedFilters AdvancedSearchFilter
        {
            get
            {
                if (_myAdvancedFilters == null)
                {
                    _myAdvancedFilters = new MyAdvancedFilters(this);                    
                }

                return _myAdvancedFilters;
            }
        }
    }

    public class MyAdvancedFilters : AdvancedFilters
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MyAdvancedFilters"/> class.
        /// </summary>
        /// <param name="principal">The source <see cref="Principal"/></param>
        public MyAdvancedFilters(Principal principal) : base(principal) { }

        public void WhereCompanyNotSet()
        {
            this.AdvancedFilterSet("company", "", typeof(string), MatchType.NotEquals);
        }
    }
}
like image 629
Jim Avatar asked Dec 07 '12 18:12

Jim


2 Answers

Modifying my AdvanceFilters class to the following gets the results I need.

public class MyAdvancedFilters : AdvancedFilters
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MyAdvancedFilters"/> class.
    /// </summary>
    /// <param name="principal">The source <see cref="Principal"/></param>
    public MyAdvancedFilters(Principal principal) : base(principal) { }

    public void WhereCompanyNotSet()
    {
        this.AdvancedFilterSet("company", "*", typeof(string), MatchType.NotEquals);
    }
}

It is the "*" for the value part in the AdvancedFilterSet.

Thanks to Sean for leading to the right trail to come up with the answer appropriate for the AccountManagement objects.

like image 107
Jim Avatar answered Nov 14 '22 07:11

Jim


Try using this as your query:

(&(objectClass=user)(!(company=*)))

Docs: http://msdn.microsoft.com/en-gb/library/windows/desktop/aa746475(v=vs.85).aspx about half way down the page it says "Get all entries without an email attribute:"

like image 26
Sean Airey Avatar answered Nov 14 '22 08:11

Sean Airey