blob: dff822a045e20aa0b0712fdc2b63e0679441977f [file] [log] [blame]
/*
* 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;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
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.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import org.apache.directory.server.core.subtree.SubentryInterceptor;
import org.apache.directory.server.unit.AbstractServerTest;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.message.AttributeImpl;
import org.apache.directory.shared.ldap.message.AttributesImpl;
import org.apache.directory.shared.ldap.message.SubentriesControl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* 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$
*/
public class SearchITest extends AbstractServerTest
{
private LdapContext ctx;
private static final String RDN = "cn=Tori Amos";
private static final String RDN2 = "cn=Rolling-Stones";
private static final String HEATHER_RDN = "cn=Heather Graham";
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 AttributesImpl();
Attribute attribute = new AttributeImpl( "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 ) );
}
}
/**
* Create context and a person entry.
*/
@Before
public void setUp() throws Exception
{
super.setUp();
Hashtable<String, String> env = new Hashtable<String, String>();
env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
env.put( "java.naming.security.principal", "uid=admin,ou=system" );
env.put( "java.naming.security.credentials", "secret" );
env.put( "java.naming.security.authentication", "simple" );
ctx = new InitialLdapContext( env, null );
assertNotNull( ctx );
// Create a person with description
Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
attributes.put( "description", "an American singer-songwriter" );
ctx.createSubcontext( RDN, attributes );
// Create a second person with description
attributes = this.getPersonAttributes( "Jagger", "Rolling-Stones" );
attributes.put( "description", "an English singer-songwriter" );
ctx.createSubcontext( RDN2, attributes );
// Create entry for Heather Graham
Attributes heather = new AttributesImpl();
Attribute ocls = new AttributeImpl( "objectClass" );
ocls.add( "top" );
ocls.add( "person" );
heather.put( ocls );
heather.put( "cn", "Heather Nova" );
heather.put( "sn", "Nova" );
ctx.createSubcontext( HEATHER_RDN, heather );
}
/**
* Remove person entry and close context.
*/
@After
public void tearDown() throws Exception
{
try
{
ctx.unbind( RDN );
}
catch ( Exception e )
{
// Do nothing
}
finally
{
ctx.close();
}
ctx = null;
super.tearDown();
}
/**
* Performs a single level search from ou=system base and
* returns the set of DNs found.
*/
private Set<String> search( String filter ) throws NamingException
{
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 NamingException
{
nbTests = 26;
// create additional entry
Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
ctx.createSubcontext( "cn=Kate Bush", attributes );
// -------------------------------------------------------------------
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.
*/
/*
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.getAttributes();
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 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 = "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 NamingException
{
// 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
*
public void testSearchWithQuotesInBase() throws NamingException {
SearchControls sctls = new SearchControls();
sctls.setSearchScope(SearchControls.OBJECT_SCOPE);
String filter = "(cn=Tori Amos)";
// sn="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
{
// create additional entry
Attributes attributes = this.getPersonAttributes( "Bush", "Kate Bush" );
ctx.createSubcontext( "cn=Kate Bush", attributes );
// -------------------------------------------------------------------
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" } );
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( "java.naming.provider.url", "ldap://localhost:" + port );
env.put( "java.naming.security.principal", "uid=admin,ou=system" );
env.put( "java.naming.security.credentials", "secret" );
env.put( "java.naming.security.authentication", "simple" );
ctx = new InitialLdapContext( env, null );
assertNotNull( ctx );
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 NamingException
{
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 AttributesImpl( "administrativeRole", SubentryInterceptor.AC_AREA, true );
adminCtx.modifyAttributes( "", DirContext.ADD_ATTRIBUTE, changes );
}
// now add the A/C subentry below ou=system
Attributes subentry = new AttributesImpl( "cn", cn, true );
Attribute objectClass = new AttributeImpl( "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 NamingException
{
// Create entry
Attributes heather = new AttributesImpl();
Attribute ocls = new AttributeImpl( "objectClass" );
ocls.add( "top" );
ocls.add( "person" );
heather.put( ocls );
heather.put( "cn", "Heather Nova" );
heather.put( "sn", "Nova" );
String rdn = "cn=Heather Nova";
ctx.createSubcontext( rdn, heather );
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 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 NamingException
{
// Create entry
Attributes kate = new AttributesImpl();
kate.put( "objectClass", "organizationalperson" );
kate.put( "cn", "Kate Bush" );
kate.put( "sn", "Bush" );
String rdn = "cn=Kate Bush";
ctx.createSubcontext( rdn, kate );
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
{
// 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( 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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
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 NamingException
{
// create administrative area
Attributes aaAttrs = new AttributesImpl();
Attribute aaObjectClass = new AttributeImpl( "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 AttributesImpl();
Attribute objectClass = new AttributeImpl( "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 NamingException
{
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( 5, attrs.size() );
assertNotNull( attrs.get( "cn" ) );
assertNotNull( attrs.get( "sn" ) );
assertNotNull( attrs.get( "objectClass" ) );
assertNotNull( attrs.get( "jpegPhoto" ) );
assertNotNull( attrs.get( "description" ) );
assertNull( attrs.get( "createtimestamp" ) );
assertNull( attrs.get( "creatorsname" ) );
}
@Test
public void testSearchOperationalAttrs() throws NamingException
{
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( 2, 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" ) );
}
@Test
public void testSearchAllAttrs() throws NamingException
{
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( 7, 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( "createtimestamp" ) );
assertNotNull( attrs.get( "creatorsname" ) );
}
@Test
public void testSearchBadDN() throws NamingException
{
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 NamingException, Exception
{
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 NamingException
{
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 NamingException
{
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 user and operational attributes are present, if both "*" and "+" are requested.
*/
@Test
public void testSearchOperationalAndUserAttributes() throws NamingException
{
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 for DIRSERVER-1183.
*
* @see https://issues.apache.org/jira/browse/DIRSERVER-1183
* @throws Exception
*/
@Test
public void testDIRSERVER_1183() throws Exception
{
Attributes attrs = new AttributesImpl( "objectClass", "inetOrgPerson", true );
attrs.get( "objectClass" ).add( "organizationalPerson" );
attrs.get( "objectClass" ).add( "person" );
attrs.put( "givenName", "Jim" );
attrs.put( "sn", "Bean" );
attrs.put( "cn", "\"Jim, Bean\"" );
ctx.createSubcontext( "cn=\"Jim, Bean\"", attrs );
}
/**
* 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
{
Attributes attrs = new AttributesImpl( "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 );
}
}
}