/*
* 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 );
}
}