Package org.apache.directory.server.operations.search

Source Code of org.apache.directory.server.operations.search.SearchIT

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.apache.directory.server.operations.search;


import static org.apache.directory.server.integ.ServerIntegrationUtils.getClientApiConnection;
import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.naming.InvalidNameException;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;

import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.exception.LdapException;
import org.apache.directory.ldap.client.api.message.SearchRequest;
import org.apache.directory.ldap.client.api.message.SearchResponse;
import org.apache.directory.server.annotations.CreateLdapServer;
import org.apache.directory.server.annotations.CreateTransport;
import org.apache.directory.server.core.annotations.ApplyLdifs;
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
import org.apache.directory.server.core.integ.FrameworkRunner;
import org.apache.directory.server.core.subtree.SubentryInterceptor;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.cursor.Cursor;
import org.apache.directory.shared.ldap.entry.Entry;
import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
import org.apache.directory.shared.ldap.filter.SearchScope;
import org.apache.directory.shared.ldap.jndi.JndiUtils;
import org.apache.directory.shared.ldap.message.control.Control;
import org.apache.directory.shared.ldap.name.DN;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;


/**
* Testcase with different modify operations on a person entry. Each includes a
* single add op only. Created to demonstrate DIREVE-241 ("Adding an already
* existing attribute value with a modify operation does not cause an error.").
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev: 682556 $
*/
@RunWith(FrameworkRunner.class)
@CreateLdapServer(
    transports =
      {
        @CreateTransport(protocol = "LDAP")
      })
@ApplyLdifs(
    {

        // Entry # 0
        "dn: cn=Kate Bush,ou=system",
        "objectClass: person",
        "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson",
        "objectClass: strongAuthenticationUser",
        "objectClass: top",
        "userCertificate:: NFZOXw==",
        "cn: Kate Bush",
        "description: this is a person",
        "sn: Bush",
        "jpegPhoto:: /9j/4AAQSkZJRgABAQEASABIAAD/4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD//gAX",
        " Q3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw",
        " 5Mzg3QEhcTkBEV0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2",
        " NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8AAEQgAAQABA",
        " wEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAAAAAAAAAF/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/E",
        " ABUBAQEAAAAAAAAAAAAAAAAAAAUG/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8",
        " AigC14//Z",

        // Entry # 2
        "dn: cn=Tori Amos,ou=system",
        "objectClass: person",
        "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson",
        "objectClass: strongAuthenticationUser",
        "objectClass: top",
        "userCertificate:: NFZOXw==",
        "cn: Tori Amos",
        "description: an American singer-songwriter",
        "sn: Amos",
        "jpegPhoto:: /9j/4AAQSkZJRgABAQEASABIAAD/4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD//gAX",
        " Q3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw",
        " 5Mzg3QEhcTkBEV0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2",
        " NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8AAEQgAAQABA",
        " wEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAAAAAAAAAF/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/E",
        " ABUBAQEAAAAAAAAAAAAAAAAAAAUG/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8",
        " AigC14//Z",

        // Entry # 3
        "dn: cn=Rolling-Stones,ou=system",
        "objectClass: person",
        "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson",
        "objectClass: strongAuthenticationUser",
        "objectClass: top",
        "userCertificate:: NFZOXw==",
        "cn: Rolling-Stones",
        "description: an English singer-songwriter",
        "sn: Jagger",
        "jpegPhoto:: /9j/4AAQSkZJRgABAQEASABIAAD/4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD//gAX",
        " Q3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw",
        " 5Mzg3QEhcTkBEV0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2",
        " NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8AAEQgAAQABA",
        " wEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAAAAAAAAAF/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/E",
        " ABUBAQEAAAAAAAAAAAAAAAAAAAUG/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8",
        " AigC14//Z",

        // Entry # 4
        "dn: cn=Heather Nova,ou=system", "objectClass: person", "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson", "objectClass: strongAuthenticationUser", "objectClass: top",
        "userCertificate:: NFZOXw==", "cn: Heather Nova",
        "sn: Nova",
        "jpegPhoto:: /9j/4AAQSkZJRgABAQEASABIAAD/4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD//gAX",
        " Q3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw",
        " 5Mzg3QEhcTkBEV0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2",
        " NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8AAEQgAAQABA",
        " wEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAAAAAAAAAF/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/E",
        " ABUBAQEAAAAAAAAAAAAAAAAAAAUG/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8",
        " AigC14//Z",

        // Entry #5
        "dn: cn=Janis Joplin,ou=system", "objectClass: person", "objectClass: organizationalPerson",
        "objectClass: inetOrgPerson", "objectClass: top", "objectClass: strongAuthenticationUser", "cn: Janis Joplin",
        "sn: Joplin", "userCertificate:: ",

        // Entry #6
        "dn: cn=Kim Wilde,ou=system", "objectClass: person", "objectClass: top", "cn: Kim Wilde", "sn: Wilde",
        "description: an American singer-songwriter+sexy blond"

    })
public class SearchIT extends AbstractLdapTestUnit
{
    private static final String BASE = "ou=system";

    public static LdapServer ldapServer;

    private static final String RDN = "cn=Tori Amos";
    private static final String RDN2 = "cn=Rolling-Stones";
    private static final String HEATHER_RDN = "cn=Heather Nova";
    private static final String FILTER = "(objectclass=*)";

    private static final byte[] JPEG = new byte[]
        { ( byte ) 0xff, ( byte ) 0xd8, ( byte ) 0xff, ( byte ) 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
            0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, ( byte ) 0xff, ( byte ) 0xe1, 0x00, 0x16, 0x45, 0x78, 0x69,
            0x66, 0x00, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            ( byte ) 0xff, ( byte ) 0xfe, 0x00, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74,
            0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4d, 0x50, ( byte ) 0xff, ( byte ) 0xdb, 0x00, 0x43, 0x00,
            0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18,
            0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48,
            0x5c, 0x4e, 0x40, 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e,
            0x4d, 0x71, 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, ( byte ) 0xff, ( byte ) 0xdb, 0x00, 0x43, 0x01,
            0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63,
            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
            0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ( byte ) 0xff, ( byte ) 0xc0, 0x00, 0x11, 0x08,
            0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, ( byte ) 0xff,
            ( byte ) 0xc4, 0x00, 0x15, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ( byte ) 0xff, ( byte ) 0xc4, 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ( byte ) 0xff, ( byte ) 0xc4,
            0x00, 0x15, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x05, 0x06, ( byte ) 0xff, ( byte ) 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ( byte ) 0xff, ( byte ) 0xda, 0x00, 0x0c, 0x03,
            0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, ( byte ) 0x8a, 0x00, ( byte ) 0xb5, ( byte ) 0xe3,
            ( byte ) 0xff, ( byte ) 0xd9, };


    /**
     * Creation of required attributes of a person entry.
     */
    private Attributes getPersonAttributes( String sn, String cn )
    {
        Attributes attributes = new BasicAttributes( true );
        Attribute attribute = new BasicAttribute( "objectClass" );
        attribute.add( "top" );
        attribute.add( "person" );
        attribute.add( "organizationalPerson" );
        attribute.add( "inetOrgPerson" );
        attributes.put( attribute );
        attributes.put( "cn", cn );
        attributes.put( "sn", sn );
        attributes.put( "jpegPhoto", JPEG );

        return attributes;
    }


    private void checkForAttributes( Attributes attrs, String[] attrNames )
    {
        for ( String attrName : attrNames )
        {
            assertNotNull( "Check if attr " + attrName + " is present", attrs.get( attrName ) );
        }
    }


    /**
     * For DIRSERVER-715 and part of DIRSERVER-169.  May include other tests
     * for binary attribute based searching.
     */
    @Test
    public void testSearchByBinaryAttribute() throws Exception
    {
        DirContext ctx = ( DirContext ) getWiredContext( ldapServer ).lookup( BASE );
        byte[] certData = new byte[]
            { 0x34, 0x56, 0x4e, 0x5f };

        // Search for kate by cn first
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        NamingEnumeration<SearchResult> enm = ctx.search( "", "(cn=Kate Bush)", controls );
        assertTrue( enm.hasMore() );
        SearchResult sr = enm.next();
        assertNotNull( sr );
        assertFalse( enm.hasMore() );
        assertEquals( "cn=Kate Bush", sr.getName() );

        enm = ctx.search( "", "(&(cn=Kate Bush)(userCertificate={0}))", new Object[]
            { certData }, controls );
        assertTrue( enm.hasMore() );
        sr = ( SearchResult ) enm.next();
        assertNotNull( sr );
        assertFalse( enm.hasMore() );
        assertEquals( "cn=Kate Bush", sr.getName() );

        enm = ctx.search( "", "(userCertificate=\\34\\56\\4E\\5F)", controls );
        assertTrue( enm.hasMore() );
        int count = 0;
        Set<String> expected = new HashSet<String>();
        expected.add( "cn=Kate Bush" );
        expected.add( "cn=Tori Amos" );
        expected.add( "cn=Rolling-Stones" );
        expected.add( "cn=Heather Nova" );

        while ( enm.hasMore() )
        {
            count++;
            sr = ( SearchResult ) enm.next();
            assertNotNull( sr );

            assertTrue( expected.contains( sr.getName() ) );
            expected.remove( sr.getName() );
        }

        assertEquals( 4, count );
        assertFalse( enm.hasMore() );
        assertEquals( 0, expected.size() );
    }


    @Test
    public void testSearch() throws Exception
    {
        LdapContext ctx = getWiredContext( ldapServer );
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
        controls.setTimeLimit( 10 );

        try
        {
            ctx.search( "myBadDN", "(objectClass=*)", controls );

            fail(); // We should get an exception here
        }
        catch ( InvalidNameException ine )
        {
            // Expected.
        }
        catch ( NamingException ne )
        {
            fail();
        }
        catch ( Exception e )
        {
            fail();
        }

        try
        {
            controls = new SearchControls();
            controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
            controls.setTimeLimit( 10 );

            NamingEnumeration<SearchResult> result = ctx.search( "ou=system", "(objectClass=*)", controls );

            assertTrue( result.hasMore() );
        }
        catch ( InvalidNameException ine )
        {
            fail();
            // Expected.
        }
        catch ( NamingException ne )
        {
            fail();
        }
    }


    /**
     * Performs a single level search from ou=system base and
     * returns the set of DNs found.
     */
    private Set<String> search( String filter ) throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        NamingEnumeration<SearchResult> ii = ctx.search( "", filter, controls );

        // collect all results
        HashSet<String> results = new HashSet<String>();
        while ( ii.hasMore() )
        {
            SearchResult result = ii.next();
            results.add( result.getName() );
        }

        return results;
    }


    @Test
    public void testDirserver635() throws Exception
    {
        // -------------------------------------------------------------------
        Set<String> results = search( "(|(cn=Kate*)(cn=Tori*))" );
        assertEquals( "returned size of results", 2, results.size() );
        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );

        // -------------------------------------------------------------------
        results = search( "(|(cn=*Amos)(cn=Kate*))" );
        assertEquals( "returned size of results", 2, results.size() );
        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );

        // -------------------------------------------------------------------
        results = search( "(|(cn=Kate Bush)(cn=Tori*))" );
        assertEquals( "returned size of results", 2, results.size() );
        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );

        // -------------------------------------------------------------------
        results = search( "(|(cn=*Amos))" );
        assertEquals( "returned size of results", 1, results.size() );
        assertTrue( "contains cn=Tori Amos", results.contains( "cn=Tori Amos" ) );
    }


    /**
     * Search operation with a base DN which contains a BER encoded value.
     */
    //@Test
    /*public void testSearchBEREncodedBase() throws NamingException
    {
        // create additional entry
        Attributes attributes = this.getPersonAttributes( "Ferry", "Bryan Ferry" );
        ctx.createSubcontext( "sn=Ferry", attributes );

        SearchControls sctls = new SearchControls();
        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        String FILTER = "(cn=Bryan Ferry)";

        // sn=Ferry with BEROctetString values
        String base = "2.5.4.4=#4665727279";

        try
        {
            // Check entry
            NamingEnumeration enm = ctx.search( base, FILTER, sctls );
            assertTrue( enm.hasMore() );
            while ( enm.hasMore() )
            {
                SearchResult sr = ( SearchResult ) enm.next();
                Attributes attrs = sr.getObject();
                Attribute sn = attrs.get( "sn" );
                assertNotNull( sn );
                assertTrue( sn.contains( "Ferry" ) );
            }
        }
        catch ( Exception e )
        {
            fail( e.getMessage() );
        }
    }*/

    /**
     * Search operation with a base DN which contains a BER encoded value.
     */
    @Test
    public void testSearchWithBackslashEscapedBase() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        // create additional entry
        Attributes attributes = this.getPersonAttributes( "Ferry", "Bryan Ferry" );
        ctx.createSubcontext( "sn=Ferry", attributes );

        SearchControls sctls = new SearchControls();
        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        String filter = "(cn=Bryan Ferry)";

        // sn=Ferry with BEROctetString values
        String base = "sn=\\46\\65\\72\\72\\79";

        try
        {
            // Check entry
            NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
            assertTrue( enm.hasMore() );
            while ( enm.hasMore() )
            {
                SearchResult sr = enm.next();
                Attributes attrs = sr.getAttributes();
                Attribute sn = attrs.get( "sn" );
                assertNotNull( sn );
                assertTrue( sn.contains( "Ferry" ) );
            }
        }
        catch ( Exception e )
        {
            fail( e.getMessage() );
        }
    }


    /**
     * Add a new attribute to a person entry.
     *
     * @throws NamingException
     */
    @Test
    public void testSearchValue() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        // Setting up search controls for compare op
        SearchControls ctls = new SearchControls();
        ctls.setReturningAttributes( new String[]
            { "*" } ); // no attributes
        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );

        // Search for all entries
        NamingEnumeration<SearchResult> results = ctx.search( RDN, "(cn=*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*)", ctls );
        assertTrue( results.hasMore() );

        // Search for all entries ending by Amos
        results = ctx.search( RDN, "(cn=*Amos)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*Amos)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries ending by amos
        results = ctx.search( RDN, "(cn=*amos)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*amos)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries starting by Tori
        results = ctx.search( RDN, "(cn=Tori*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=Tori*)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries starting by tori
        results = ctx.search( RDN, "(cn=tori*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=tori*)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries containing ori
        results = ctx.search( RDN, "(cn=*ori*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*ori*)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries containing o and i
        results = ctx.search( RDN, "(cn=*o*i*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*o*i*)", ctls );
        assertTrue( results.hasMore() );

        // Search for all entries containing o, space and o
        results = ctx.search( RDN, "(cn=*o* *o*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*o* *o*)", ctls );
        assertFalse( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*o*-*o*)", ctls );
        assertTrue( results.hasMore() );

        // Search for all entries starting by To and containing A
        results = ctx.search( RDN, "(cn=To*A*)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=To*A*)", ctls );
        assertFalse( results.hasMore() );

        // Search for all entries ending by os and containing ri
        results = ctx.search( RDN, "(cn=*ri*os)", ctls );
        assertTrue( results.hasMore() );

        results = ctx.search( RDN2, "(cn=*ri*os)", ctls );
        assertFalse( results.hasMore() );
    }


    /**
     * Search operation with a base DN with quotes
     *
    @Test
    public void testSearchWithQuotesInBase() throws NamingException {

        SearchControls sctls = new SearchControls();
        sctls.setSearchScope(SearchControls.OBJECT_SCOPE);
        String filter = "(cn=Tori Amos)";

        // cn="Tori Amos" (with quotes)
        String base = "cn=\"Tori Amos\"";

        try {
            // Check entry
            NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
            assertTrue( enm.hasMore() );
           
            while ( enm.hasMore() ) {
                SearchResult sr = enm.next();
                Attributes attrs = sr.getAttributes();
                Attribute sn = attrs.get("sn");
                assertNotNull(sn);
                assertTrue( sn.contains( "Amos" ) );
            }
        } catch (Exception e) {
            fail( e.getMessage() );
        }
    }
   
   
    /**
     * Tests for <a href="http://issues.apache.org/jira/browse/DIRSERVER-645">
     * DIRSERVER-645<\a>: Wrong search FILTER evaluation with AND
     * operator and undefined operands.
     */
    @Test
    public void testUndefinedAvaInBranchFilters() throws Exception
    {
        // -------------------------------------------------------------------
        Set<String> results = search( "(|(sn=Bush)(numberOfOctaves=4))" );
        assertEquals( "returned size of results", 1, results.size() );
        assertTrue( "contains cn=Kate Bush", results.contains( "cn=Kate Bush" ) );

        // if numberOfOctaves is undefined then this whole FILTER is undefined
        results = search( "(&(sn=Bush)(numberOfOctaves=4))" );
        assertEquals( "returned size of results", 0, results.size() );
    }


    @Test
    public void testSearchSchema() throws Exception
    {
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
        controls.setReturningAttributes( new String[]
            { "objectClasses" } );

        LdapContext ctx = getWiredContext( ldapServer );

        NamingEnumeration<SearchResult> results = ctx.search( "cn=schema", "objectClass=subschema", controls );
        assertTrue( results.hasMore() );
        SearchResult result = results.next();
        assertNotNull( result );
        assertFalse( results.hasMore() );

        NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();

        while ( attrs.hasMoreElements() )
        {
            Attribute attr = ( Attribute ) attrs.next();
            String ID = attr.getID();
            assertEquals( "objectClasses", ID );
        }

        assertNotNull( result.getAttributes().get( "objectClasses" ) );
        assertEquals( 1, result.getAttributes().size() );
    }


    /**
     * Creates an access control subentry under ou=system whose subtree covers
     * the entire naming context.
     *
     * @param cn the common name and rdn for the subentry
     * @param subtree the subtreeSpecification for the subentry
     * @param aciItem the prescriptive ACI attribute value
     * @throws NamingException if there is a problem creating the subentry
     */
    private void createAccessControlSubentry( String cn, String subtree, String aciItem ) throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        DirContext adminCtx = ctx;

        // modify ou=system to be an AP for an A/C AA if it is not already
        Attributes ap = adminCtx.getAttributes( "", new String[]
            { "administrativeRole" } );
        Attribute administrativeRole = ap.get( "administrativeRole" );
        if ( administrativeRole == null || !administrativeRole.contains( SubentryInterceptor.AC_AREA ) )
        {
            Attributes changes = new BasicAttributes( "administrativeRole", SubentryInterceptor.AC_AREA, true );
            adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
        }

        // now add the A/C subentry below ou=system
        Attributes subentry = new BasicAttributes( "cn", cn, true );
        Attribute objectClass = new BasicAttribute( "objectClass" );
        subentry.put( objectClass );
        objectClass.add( "top" );
        objectClass.add( SchemaConstants.SUBENTRY_OC );
        objectClass.add( "accessControlSubentry" );
        subentry.put( "subtreeSpecification", subtree );
        subentry.put( "prescriptiveACI", aciItem );
        adminCtx.createSubcontext( "cn=" + cn, subentry );
    }


    /**
     * Test case to demonstrate DIRSERVER-705 ("object class top missing in search
     * result, if scope is base and attribute objectClass is requested explicitly").
     */
    @Test
    public void testAddWithObjectclasses() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls ctls = new SearchControls();
        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "objectclass" } );
        String filter = "(objectclass=*)";
        String rdn = "cn=Kim Wilde";

        NamingEnumeration<SearchResult> result = ctx.search( rdn, filter, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = result.next();
            Attributes heatherReloaded = entry.getAttributes();
            Attribute loadedOcls = heatherReloaded.get( "objectClass" );
            assertNotNull( loadedOcls );
            assertTrue( loadedOcls.contains( "person" ) );
            assertTrue( loadedOcls.contains( "top" ) );
        }
        else
        {
            fail( "entry " + rdn + " not found" );
        }

        ctx.destroySubcontext( rdn );
    }


    /**
     * Test case to demonstrate DIRSERVER-705 ("object class top missing in search
     * result, if scope is base and attribute objectClass is requested explicitly").
     */
    @Test
    public void testAddWithMissingObjectclasses() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        String rdn = "cn=Kate Bush";
        SearchControls ctls = new SearchControls();
        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "objectclass" } );
        String filter = "(objectclass=*)";

        NamingEnumeration<SearchResult> result = ctx.search( rdn, filter, ctls );
        if ( result.hasMore() )
        {
            SearchResult entry = result.next();
            Attributes kateReloaded = entry.getAttributes();
            Attribute loadedOcls = kateReloaded.get( "objectClass" );
            assertNotNull( loadedOcls );
            assertTrue( loadedOcls.contains( "top" ) );
            assertTrue( loadedOcls.contains( "person" ) );
            assertTrue( loadedOcls.contains( "organizationalPerson" ) );

        }
        else
        {
            fail( "entry " + rdn + " not found" );
        }

        ctx.destroySubcontext( rdn );
    }


    @Test
    public void testSubentryControl() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        // create a real access control subentry
        createAccessControlSubentry( "anyBodyAdd", "{}", "{ " + "  identificationTag \"addAci\", "
            + "  precedence 14, " + "  authenticationLevel none, " + "  itemOrUserFirst userFirst: " + "  { "
            + "    userClasses " + "    { " + "      allUsers " + "    }, " + "    userPermissions " + "    { "
            + "      { " + "        protectedItems " + "        { " + "          entry, allUserAttributeTypesAndValues"
            + "        }, " + "        grantsAndDenials " + "        { " + "          grantAdd, grantBrowse "
            + "        } " + "      } " + "    } " + "  } " + "}" );

        // prepare the subentry control to make the subentry visible
        SubentriesControl control = new SubentriesControl();
        control.setVisibility( true );
        Control[] reqControls = new Control[]
            { control };
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );

        ctx.setRequestControls( JndiUtils.toJndiControls( reqControls ) );
        NamingEnumeration<SearchResult> enm = ctx.search( "", "(objectClass=*)", searchControls );
        Set<String> results = new HashSet<String>();

        while ( enm.hasMore() )
        {
            SearchResult result = enm.next();
            results.add( result.getName() );
        }

        assertEquals( "expected results size of", 1, results.size() );
        assertTrue( results.contains( "cn=anyBodyAdd" ) );
    }


    /**
     * Create a person entry with multivalued RDN and check its content. This
     * testcase was created to demonstrate DIRSERVER-628.
     */
    @Test
    public void testMultiValuedRdnContent() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
        String rdn = "cn=Kate Bush+sn=Bush";
        ctx.createSubcontext( rdn, attrs );

        SearchControls sctls = new SearchControls();
        sctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
        String filter = "(sn=Bush)";
        String base = "";

        NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
        while ( enm.hasMore() )
        {
            SearchResult sr = enm.next();
            attrs = sr.getAttributes();
            Attribute cn = sr.getAttributes().get( "cn" );
            assertNotNull( cn );
            assertTrue( cn.contains( "Kate Bush" ) );
            Attribute sn = sr.getAttributes().get( "sn" );
            assertNotNull( sn );
            assertTrue( sn.contains( "Bush" ) );
        }

        ctx.destroySubcontext( rdn );
    }


    /**
     * Create a person entry with multivalued RDN and check its name.
     */
    @Test
    public void testMultiValuedRdnName() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" );
        String rdn = "cn=Kate Bush+sn=Bush";
        DirContext entry = ctx.createSubcontext( rdn, attrs );
        String nameInNamespace = entry.getNameInNamespace();

        SearchControls sctls = new SearchControls();
        sctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        String filter = "(sn=Bush)";
        String base = rdn;

        NamingEnumeration<SearchResult> enm = ctx.search( base, filter, sctls );
        if ( enm.hasMore() )
        {
            SearchResult sr = enm.next();
            assertNotNull( sr );
            assertEquals( "Name in namespace", nameInNamespace, sr.getNameInNamespace() );
        }
        else
        {
            fail( "Entry not found:" + nameInNamespace );
        }

        ctx.destroySubcontext( rdn );
    }


    @Test
    public void testSearchJpeg() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        NamingEnumeration<SearchResult> res = ctx.search( "", "(cn=Tori*)", controls );

        // collect all results
        while ( res.hasMore() )
        {
            SearchResult result = res.next();

            Attributes attrs = result.getAttributes();

            NamingEnumeration<? extends Attribute> all = attrs.getAll();

            while ( all.hasMoreElements() )
            {
                Attribute attr = all.next();

                if ( "jpegPhoto".equalsIgnoreCase( attr.getID() ) )
                {
                    byte[] jpegVal = ( byte[] ) attr.get();

                    assertTrue( Arrays.equals( jpegVal, JPEG ) );
                }
            }
        }
    }


    @Test
    public void testSearchOID() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        NamingEnumeration<SearchResult> res = ctx.search( "", "(2.5.4.3=Tori*)", controls );

        // ensure that the entry "cn=Tori Amos" was found
        assertTrue( res.hasMore() );

        SearchResult result = ( SearchResult ) res.next();

        // ensure that result is not null
        assertNotNull( result );

        String rdn = result.getName();

        // ensure that the entry "cn=Tori Amos" was found
        assertEquals( "cn=Tori Amos", rdn );

        // ensure that no other value was found
        assertFalse( res.hasMore() );
    }


    @Test
    public void testSearchAttrCN() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "cn" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure the one and only attribute is "cn"
        assertEquals( 1, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertEquals( 1, attrs.get( "cn" ).size() );
        assertEquals( "Tori Amos", ( String ) attrs.get( "cn" ).get() );
    }


    @Test
    public void testSearchAttrName() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "name" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure that "cn" and "sn" are returned
        assertEquals( 2, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertEquals( 1, attrs.get( "cn" ).size() );
        assertEquals( "Tori Amos", ( String ) attrs.get( "cn" ).get() );
        assertNotNull( attrs.get( "sn" ) );
        assertEquals( 1, attrs.get( "sn" ).size() );
        assertEquals( "Amos", ( String ) attrs.get( "sn" ).get() );
    }


    @Test
    public void testSearchAttrCommonName() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "commonName" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // requested attribute was "commonName", but ADS returns "cn".
        //       Other servers do the following:
        //       - OpenLDAP: also return "cn"
        //       - Siemens DirX: return "commonName"
        //       - Sun Directory 5.2: return "commonName"
        // ensure the one and only attribute is "cn"
        assertEquals( 1, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertEquals( 1, attrs.get( "cn" ).size() );
        assertEquals( "Tori Amos", ( String ) attrs.get( "cn" ).get() );
    }


    @Test
    public void testSearchAttrOID() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "2.5.4.3" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori*)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // requested attribute was "2.5.4.3", but ADS returns "cn".
        //       Other servers do the following:
        //       - OpenLDAP: also return "cn"
        //       - Siemens DirX: also return "cn"
        //       - Sun Directory 5.2: return "2.5.4.3"
        // ensure the one and only attribute is "cn"
        assertEquals( 1, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertEquals( 1, attrs.get( "cn" ).size() );
        assertEquals( "Tori Amos", ( String ) attrs.get( "cn" ).get() );
    }


    @Test
    public void testSearchAttrC_L() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        // create administrative area
        Attributes aaAttrs = new BasicAttributes( true );
        Attribute aaObjectClass = new BasicAttribute( "objectClass" );
        aaObjectClass.add( "top" );
        aaObjectClass.add( "organizationalUnit" );
        aaObjectClass.add( "extensibleObject" );
        aaAttrs.put( aaObjectClass );
        aaAttrs.put( "ou", "Collective Area" );
        aaAttrs.put( "administrativeRole", "collectiveAttributeSpecificArea" );
        DirContext aaCtx = ctx.createSubcontext( "ou=Collective Area", aaAttrs );

        // create subentry
        Attributes subentry = new BasicAttributes( true );
        Attribute objectClass = new BasicAttribute( "objectClass" );
        objectClass.add( "top" );
        objectClass.add( SchemaConstants.SUBENTRY_OC );
        objectClass.add( "collectiveAttributeSubentry" );
        subentry.put( objectClass );
        subentry.put( "c-l", "Munich" );
        subentry.put( "cn", "Collective Subentry" );
        subentry.put( "subtreeSpecification", "{ }" );
        aaCtx.createSubcontext( "cn=Collective Subentry", subentry );

        // create real enty
        Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
        aaCtx.createSubcontext( "cn=Kate Bush", attributes );

        // search
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "c-l" } );

        NamingEnumeration<SearchResult> res = aaCtx.search( "", "(cn=Kate Bush)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure the one and only attribute is "c-l"
        assertEquals( 1, attrs.size() );
        assertNotNull( attrs.get( "c-l" ) );
        assertEquals( 1, attrs.get( "c-l" ).size() );
        assertEquals( "Munich", ( String ) attrs.get( "c-l" ).get() );
    }


    @Test
    public void testSearchUsersAttrs() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "*" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure that all user attributes are returned
        assertEquals( 6, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertNotNull( attrs.get( "sn" ) );
        assertNotNull( attrs.get( "objectClass" ) );
        assertNotNull( attrs.get( "jpegPhoto" ) );
        assertNotNull( attrs.get( "description" ) );
        assertNotNull( attrs.get( "userCertificate" ) );
        assertNull( attrs.get( "createtimestamp" ) );
        assertNull( attrs.get( "creatorsname" ) );
    }


    @Test
    public void testSearchOperationalAttrs() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "+" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure that all operational attributes are returned
        // and no user attributes
        assertEquals( 4, attrs.size() );
        assertNull( attrs.get( "cn" ) );
        assertNull( attrs.get( "sn" ) );
        assertNull( attrs.get( "objectClass" ) );
        assertNull( attrs.get( "jpegPhoto" ) );
        assertNull( attrs.get( "description" ) );
        assertNotNull( attrs.get( "createtimestamp" ) );
        assertNotNull( attrs.get( "creatorsname" ) );
        assertNotNull( attrs.get( "entryuuid" ) );
        assertNotNull( attrs.get( "entrycsn" ) );
    }


    @Test
    public void testSearchAllAttrs() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "+", "*" } );

        NamingEnumeration<SearchResult> res = ctx.search( "", "(commonName=Tori Amos)", controls );

        assertTrue( res.hasMore() );

        SearchResult result = ( SearchResult ) res.next();

        // ensure that result is not null
        assertNotNull( result );

        Attributes attrs = result.getAttributes();

        // ensure that all user attributes are returned
        assertEquals( 10, attrs.size() );
        assertNotNull( attrs.get( "cn" ) );
        assertNotNull( attrs.get( "sn" ) );
        assertNotNull( attrs.get( "objectClass" ) );
        assertNotNull( attrs.get( "jpegPhoto" ) );
        assertNotNull( attrs.get( "userCertificate" ) );
        assertNotNull( attrs.get( "description" ) );
        assertNotNull( attrs.get( "createtimestamp" ) );
        assertNotNull( attrs.get( "creatorsname" ) );
        assertNotNull( attrs.get( "entryuuid" ) );
        assertNotNull( attrs.get( "entrycsn" ) );
    }


    @Test
    public void testSearchBadDN() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );

        try
        {
            ctx.search( "cn=admin", "(objectClass=*)", controls );
        }
        catch ( NameNotFoundException nnfe )
        {
            assertTrue( true );
        }
    }


    @Test
    public void testSearchInvalidDN() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );

        try
        {
            ctx.search( "myBadDN", "(objectClass=*)", controls );
            fail();
        }
        catch ( NamingException ne )
        {
            assertTrue( true );
        }
    }


    /**
     * Check if operational attributes are present, if "+" is requested.
     */
    @Test
    public void testSearchOperationalAttributes() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "+" } );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = result.next();

            String[] opAttrNames =
                { "creatorsName", "createTimestamp" };

            checkForAttributes( entry.getAttributes(), opAttrNames );
        }
        else
        {
            fail( "entry " + HEATHER_RDN + " not found" );
        }

        result.close();
    }


    /**
     * Check if user attributes are present, if "*" is requested.
     */
    @Test
    public void testSearchUserAttributes() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "*" } );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = result.next();

            String[] userAttrNames =
                { "objectClass", "sn", "cn" };

            checkForAttributes( entry.getAttributes(), userAttrNames );
        }
        else
        {
            fail( "entry " + HEATHER_RDN + " not found" );
        }

        result.close();
    }


    /**
     * Check if no error occurs if " " is requested.
     */
    @Test
    public void testSearchUserAttributes_Space() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { " " } );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
        result.close();
    }


    /**
     * Check if no error occurs if "" is requested.
     */
    @Test
    public void testSearchUserAttributes_EmptyAttrs() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "" } );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
        result.close();
    }


    /**
     * Check if no error occurs if "" is requested.
     */
    @Test
    public void testSearchUserAttributes_NullAttrs() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[0] );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );
        result.close();
    }


    /**
     * Check if user and operational attributes are present, if both "*" and "+" are requested.
     */
    @Test
    public void testSearchOperationalAndUserAttributes() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "+", "*" } );

        String[] userAttrNames =
            { "objectClass", "sn", "cn" };

        String[] opAttrNames =
            { "creatorsName", "createTimestamp" };

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = result.next();
            Attributes attrs = entry.getAttributes();

            assertNotNull( attrs );

            checkForAttributes( attrs, userAttrNames );
            checkForAttributes( attrs, opAttrNames );
        }
        else
        {
            fail( "entry " + HEATHER_RDN + " not found" );
        }

        result.close();

        ctls.setReturningAttributes( new String[]
            { "*", "+" } );

        result = ctx.search( HEATHER_RDN, FILTER, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = ( SearchResult ) result.next();
            Attributes attrs = entry.getAttributes();

            assertNotNull( attrs );

            checkForAttributes( attrs, userAttrNames );
            checkForAttributes( attrs, opAttrNames );
        }
        else
        {
            fail( "entry " + HEATHER_RDN + " not found" );
        }

        result.close();
    }


    @Test
    public void testSubstringSearchWithEscapedCharsInFilter() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        Attributes attrs = new BasicAttributes( "objectClass", "inetOrgPerson", true );
        attrs.get( "objectClass" ).add( "organizationalPerson" );
        attrs.get( "objectClass" ).add( "person" );
        attrs.put( "givenName", "Jim" );
        attrs.put( "sn", "Bean" );
        attrs.put( "cn", "jimbean" );
        attrs.put( "description", "(sex*pis\\tols)" );
        ctx.createSubcontext( "cn=jimbean", attrs );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "cn" } );

        String[] filters = new String[]
            { "(description=*\\28*)", "(description=*\\29*)", "(description=*\\2A*)", "(description=*\\5C*)" };
        for ( String filter : filters )
        {
            NamingEnumeration<SearchResult> res = ctx.search( "", filter, controls );
            assertTrue( res.hasMore() );
            SearchResult result = res.next();
            assertNotNull( result );
            attrs = result.getAttributes();
            assertEquals( 1, attrs.size() );
            assertNotNull( attrs.get( "cn" ) );
            assertEquals( 1, attrs.get( "cn" ).size() );
            assertEquals( "jimbean", ( String ) attrs.get( "cn" ).get() );
            assertFalse( res.hasMore() );
        }
    }


    /**
     * Test for DIRSERVER-1180 where search hangs when an invalid a substring
     * expression missing an any field is used in a filter: i.e. (cn=**).
     *
     * @see https://issues.apache.org/jira/browse/DIRSERVER-1180
     */
    @Test
    public void testMissingAnyInSubstring_DIRSERVER_1180() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        Attributes attrs = new BasicAttributes( "objectClass", "inetOrgPerson", true );
        attrs.get( "objectClass" ).add( "organizationalPerson" );
        attrs.get( "objectClass" ).add( "person" );
        attrs.put( "givenName", "Jim" );
        attrs.put( "sn", "Bean" );
        attrs.put( "cn", "jimbean" );

        ctx.createSubcontext( "cn=jimbean", attrs );

        try
        {
            ctx.search( "", "(cn=**)", new SearchControls() );
            fail();
        }
        catch ( Exception e )
        {
            assertTrue( true );
        }
    }


    @Test
    public void testSubstringSearchWithEscapedAsterisksInFilter_DIRSERVER_1181() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        Attributes vicious = new BasicAttributes( true );
        Attribute ocls = new BasicAttribute( "objectClass" );
        ocls.add( "top" );
        ocls.add( "person" );
        vicious.put( ocls );
        vicious.put( "cn", "x*y*z*" );
        vicious.put( "sn", "x*y*z*" );
        ctx.createSubcontext( "cn=x*y*z*", vicious );

        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
        controls.setReturningAttributes( new String[]
            { "cn" } );
        NamingEnumeration<SearchResult> res;

        res = ctx.search( "", "(cn=*x\\2Ay\\2Az\\2A*)", controls );
        assertTrue( res.hasMore() );
        assertEquals( "x*y*z*", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );

        res = ctx.search( "", "(cn=*{0}*)", new String[]
            { "x*y*z*" }, controls );
        assertTrue( res.hasMore() );
        assertEquals( "x*y*z*", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );
    }


    /**
     * Test for DIRSERVER-1347: Unicode characters in filter value.
     */
    @Test
    public void testUnicodeFilter_DIRSERVER_1347() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );

        Attributes groupOfNames = new BasicAttributes( true );
        Attribute groupOfNamesOC = new BasicAttribute( "objectClass" );
        groupOfNamesOC.add( "top" );
        groupOfNamesOC.add( "groupOfNames" );
        groupOfNames.put( groupOfNamesOC );
        groupOfNames.put( "cn", "groupOfNames" );
        Attribute member = new BasicAttribute( "member" );
        member.add( "uid=test,ou=system" );
        member.add( "uid=r\u00e9dacteur1,ou=system" );
        groupOfNames.put( member );
        ctx.createSubcontext( "cn=groupOfNames", groupOfNames );

        Attributes groupOfUniqueNames = new BasicAttributes( true );
        Attribute groupOfUniqueNamesOC = new BasicAttribute( "objectClass" );
        groupOfUniqueNamesOC.add( "top" );
        groupOfUniqueNamesOC.add( "groupOfUniqueNames" );
        groupOfUniqueNames.put( groupOfUniqueNamesOC );
        groupOfUniqueNames.put( "cn", "groupOfUniqueNames" );
        Attribute uniqueMember = new BasicAttribute( "uniqueMember" );
        uniqueMember.add( "uid=test,ou=system" );
        uniqueMember.add( "uid=r\u00e9dacteur1,ou=system" );
        groupOfUniqueNames.put( uniqueMember );
        ctx.createSubcontext( "cn=groupOfUniqueNames", groupOfUniqueNames );

        SearchControls controls = new SearchControls();
        NamingEnumeration<SearchResult> res;

        // search with unicode filter value
        res = ctx.search( "", "(member=uid=r\u00e9dacteur1,ou=system)", controls );
        assertTrue( res.hasMore() );
        assertEquals( "groupOfNames", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );

        // search with escaped filter value
        res = ctx.search( "", "(member=uid=r\\c3\\a9dacteur1,ou=system)", controls );
        assertTrue( res.hasMore() );
        assertEquals( "groupOfNames", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );

        // search with unicode filter value
        res = ctx.search( "", "(uniqueMember=uid=r\u00e9dacteur1,ou=system)", controls );
        assertTrue( res.hasMore() );
        assertEquals( "groupOfUniqueNames", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );

        // search with escaped filter value
        res = ctx.search( "", "(uniqueMember=uid=r\\c3\\a9dacteur1,ou=system)", controls );
        assertTrue( res.hasMore() );
        assertEquals( "groupOfUniqueNames", res.next().getAttributes().get( "cn" ).get() );
        assertFalse( res.hasMore() );
    }


    /**
     * Check if no user attributes are present, if "1.1" is requested.
     */
    @Test
    public void testSearchUserAttributes_1_1() throws Exception
    {
        LdapContext ctx = ( LdapContext ) getWiredContext( ldapServer ).lookup( BASE );
        SearchControls ctls = new SearchControls();

        ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
        ctls.setReturningAttributes( new String[]
            { "1.1" } );

        NamingEnumeration<SearchResult> result = ctx.search( HEATHER_RDN, FILTER, ctls );

        if ( result.hasMore() )
        {
            SearchResult entry = result.next();
            assertEquals( "No user attributes expected when requesting attribute 1.1", 0, entry.getAttributes().size() );
        }
        else
        {
            fail( "entry " + HEATHER_RDN + " not found" );
        }

        result.close();
    }


    /**
     * test an abandonned search request.
     */
    @Test
    public void testAbandonnedRequest() throws Exception
    {
        LdapConnection asyncCnx = new LdapConnection( "localhost", ldapServer.getPort() );

        try
        {
            // Use the client API as JNDI cannot be used to do a search without
            // first binding. (hmmm, even client API won't allow searching without binding)
            asyncCnx.bind( "uid=admin,ou=system", "secret" );

            // First, add 1000 entries in the server
            for ( int i = 0; i < 1000; i++ )
            {
                String dn = "cn=user" + i + "," + BASE;
                Entry kate = new DefaultClientEntry( new DN( dn ) );

                kate.add( "objectclass", "top", "person" );
                kate.add( "sn", "Bush" );
                kate.add( "cn", "user" + i );

                asyncCnx.add( kate );
            }

            // Searches for all the entries in ou=system
            Cursor<SearchResponse> cursor = asyncCnx.search( "ou=system", "(ObjectClass=*)", SearchScope.SUBTREE, "*" );

            // Now loop on all the elements found, and abandon after 10 elements returned
            int count = 0;

            while ( cursor.next() )
            {
                count++;

                if ( count == 10 )
                {
                    // the message ID = 1 bind op + 1000 add ops + 1 search op
                    asyncCnx.abandon( 1002 );
                }
            }

            assertEquals( 10, count );
        }
        catch ( LdapException e )
        {
            e.printStackTrace();
            fail( "Should not have caught exception." );
        }
        finally
        {
            asyncCnx.unBind();
        }
    }


    @Test
    public void testSearchSubstringWithPlus() throws Exception
    {
        LdapContext ctx = getWiredContext( ldapServer );
        SearchControls controls = new SearchControls();
        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
        controls.setTimeLimit( 10 );

        NamingEnumeration<SearchResult> result = ctx.search( "ou=system", "(description=*+*)", controls );

        assertTrue( result.hasMore() );

        SearchResult entry = result.next();

        assertEquals( "Kim Wilde", entry.getAttributes().get( "cn" ).get() );
    }


    @Test
    public void testSearchSizeLimit() throws Exception
    {
        long sizeLimit = 7;
        LdapConnection connection = getClientApiConnection( ldapServer );
        SearchRequest req = new SearchRequest();
        req.setBaseDn( "ou=system" );
        req.setFilter( "(ou=*)" );
        req.setScope( SearchScope.SUBTREE );
        req.setSizeLimit( sizeLimit );

        Cursor<SearchResponse> cursor = connection.search( req );
        long i = 0;
       
        while ( cursor.next() )
        {
            ++i;
        }

        assertEquals( sizeLimit, i );
    }


    @Test
    @Ignore( "This test is failing because of the timing issue. Note that the SearchHandler handles time based searches correctly, this is just the below test's problem" )
    public void testSearchTimeLimit() throws Exception, InterruptedException
    {
        LdapConnection connection = getClientApiConnection( ldapServer );
        SearchRequest req = new SearchRequest();
        req.setBaseDn( "ou=schema" );
        req.setFilter( "(objectClass=*)" );
        req.setScope( SearchScope.SUBTREE );

        Cursor<SearchResponse> cursor = connection.search( req );
        int count = 0;
        while ( cursor.next() )
        {
            ++count;
        }
        cursor.close();

        req.setTimeLimit( 1 );
        cursor = connection.search( req );
        int newCount = 0;
        while ( cursor.next() )
        {
            ++newCount;
        }

        assertTrue( newCount < count );
    }
}
TOP

Related Classes of org.apache.directory.server.operations.search.SearchIT

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.