1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.operations.search;
21  
22  
23  import org.apache.directory.server.core.DefaultDirectoryService;
24  import org.apache.directory.server.core.DirectoryService;
25  import org.apache.directory.server.core.entry.ServerEntry;
26  import org.apache.directory.server.core.integ.IntegrationUtils;
27  import org.apache.directory.server.core.integ.Level;
28  import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
29  import org.apache.directory.server.core.integ.annotations.CleanupLevel;
30  import org.apache.directory.server.core.integ.annotations.Factory;
31  import org.apache.directory.server.xdbm.Index;
32  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
33  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
34  import org.apache.directory.server.integ.LdapServerFactory;
35  import org.apache.directory.server.integ.SiRunner;
36  import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
37  
38  import org.apache.directory.server.ldap.LdapService;
39  import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
40  import org.apache.directory.server.ldap.handlers.bind.SimpleMechanismHandler;
41  import org.apache.directory.server.ldap.handlers.bind.cramMD5.CramMd5MechanismHandler;
42  import org.apache.directory.server.ldap.handlers.bind.digestMD5.DigestMd5MechanismHandler;
43  import org.apache.directory.server.ldap.handlers.bind.gssapi.GssapiMechanismHandler;
44  import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
45  import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
46  import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
47  import org.apache.directory.server.protocol.shared.SocketAcceptor;
48  import org.apache.directory.shared.ldap.constants.SchemaConstants;
49  import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
50  import org.apache.mina.util.AvailablePortFinder;
51  import org.junit.Test;
52  import org.junit.runner.RunWith;
53  
54  import javax.naming.NamingEnumeration;
55  import javax.naming.directory.DirContext;
56  import javax.naming.directory.SearchControls;
57  import javax.naming.directory.SearchResult;
58  
59  import java.util.HashMap;
60  import java.util.HashSet;
61  import java.util.Map;
62  import java.util.Set;
63  
64  import static org.junit.Assert.assertTrue;
65  import static org.junit.Assert.assertEquals;
66  import static org.junit.Assert.assertFalse;
67  
68  
69  /**
70   * A set of tests to make sure the negation operator is working 
71   * properly when included in search filters on indexed attributes.
72   * 
73   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
74   * @version $Rev$, $Date$
75   */
76  @RunWith ( SiRunner.class ) 
77  @CleanupLevel ( Level.CLASS )
78  @Factory ( IndexedNegationSearchIT.Factory.class )
79  @ApplyLdifs( {
80      "dn: ou=test,ou=system\n" +
81      "objectClass: top\n" +
82      "objectClass: organizationalUnit\n" +
83      "ou: test\n\n" +
84  
85      "dn: uid=test1,ou=test,ou=system\n" +
86      "objectClass: top\n" +
87      "objectClass: account\n" +
88      "uid: test1\n" +
89      "ou: test1\n\n" +
90  
91      "dn: uid=test2,ou=test,ou=system\n" +
92      "objectClass: top\n" +
93      "objectClass: account\n" +
94      "uid: test2\n" +
95      "ou: test2\n\n" +
96  
97      "dn: uid=testNoOU,ou=test,ou=system\n" +
98      "objectClass: top\n" +
99      "objectClass: account\n" +
100     "uid: testNoOU\n\n" +
101     
102     "dn: ou=actors,ou=system\n" +
103     "objectClass: top\n" +
104     "objectClass: organizationalUnit\n" +
105     "ou: actors\n\n" +
106 
107     "dn: uid=jblack,ou=actors,ou=system\n" +
108     "objectClass: top\n" +
109     "objectClass: person\n" +
110     "objectClass: organizationalPerson\n" +
111     "objectClass: uidObject\n" +
112     "uid: jblack\n" +
113     "ou: comedy\n" +
114     "ou: adventure\n" +
115     "cn: Jack Black\n" +
116     "sn: Black\n\n" +
117 
118     "dn: uid=bpitt,ou=actors,ou=system\n" +
119     "objectClass: top\n" +
120     "objectClass: person\n" +
121     "objectClass: organizationalPerson\n" +
122     "objectClass: uidObject\n" +
123     "uid: bpitt\n" +
124     "ou: drama\n" +
125     "ou: adventure\n" +
126     "cn: Brad Pitt\n" +
127     "sn: Pitt\n\n" +
128 
129     "dn: uid=gcloony,ou=actors,ou=system\n" +
130     "objectClass: top\n" +
131     "objectClass: person\n" +
132     "objectClass: organizationalPerson\n" +
133     "objectClass: uidObject\n" +
134     "uid: gcloony\n" +
135     "ou: drama\n" +
136     "cn: Goerge Cloony\n" +
137     "sn: Cloony\n\n" +
138 
139     "dn: uid=jnewbie,ou=actors,ou=system\n" +
140     "objectClass: top\n" +
141     "objectClass: person\n" +
142     "objectClass: organizationalPerson\n" +
143     "objectClass: uidObject\n" +
144     "uid: jnewbie\n" +
145     "cn: Joe Newbie\n" +
146     "sn: Newbie\n\n" 
147 
148     }
149 )
150 public class IndexedNegationSearchIT 
151 {
152     public static LdapService ldapService;
153 
154     
155     public static class Factory implements LdapServerFactory
156     {
157         public LdapService newInstance() throws Exception
158         {
159             DirectoryService service = new DefaultDirectoryService();
160             IntegrationUtils.doDelete( service.getWorkingDirectory() );
161             service.getChangeLog().setEnabled( true );
162             service.setShutdownHookEnabled( false );
163 
164             JdbmPartition system = new JdbmPartition();
165             system.setId( "system" );
166 
167             // @TODO need to make this configurable for the system partition
168             system.setCacheSize( 500 );
169 
170             system.setSuffix( "ou=system" );
171 
172             // Add indexed attributes for system partition
173             Set<Index<?,ServerEntry>> indexedAttrs = new HashSet<Index<?,ServerEntry>>();
174             indexedAttrs.add( new JdbmIndex<String,ServerEntry>( SchemaConstants.OBJECT_CLASS_AT ) );
175             indexedAttrs.add( new JdbmIndex<String,ServerEntry>( SchemaConstants.OU_AT ) );
176             system.setIndexedAttributes( indexedAttrs );
177             service.setSystemPartition( system );
178 
179             // change the working directory to something that is unique
180             // on the system and somewhere either under target directory
181             // or somewhere in a temp area of the machine.
182 
183             LdapService ldapService = new LdapService();
184             ldapService.setDirectoryService( service );
185             ldapService.setSocketAcceptor( new SocketAcceptor( null ) );
186             ldapService.setIpPort( AvailablePortFinder.getNextAvailable( 1024 ) );
187             ldapService.addExtendedOperationHandler( new StartTlsHandler() );
188             ldapService.addExtendedOperationHandler( new StoredProcedureExtendedOperationHandler() );
189 
190             // Setup SASL Mechanisms
191             
192             Map<String, MechanismHandler> mechanismHandlerMap = new HashMap<String,MechanismHandler>();
193             mechanismHandlerMap.put( SupportedSaslMechanisms.PLAIN, new SimpleMechanismHandler() );
194 
195             CramMd5MechanismHandler cramMd5MechanismHandler = new CramMd5MechanismHandler();
196             mechanismHandlerMap.put( SupportedSaslMechanisms.CRAM_MD5, cramMd5MechanismHandler );
197 
198             DigestMd5MechanismHandler digestMd5MechanismHandler = new DigestMd5MechanismHandler();
199             mechanismHandlerMap.put( SupportedSaslMechanisms.DIGEST_MD5, digestMd5MechanismHandler );
200 
201             GssapiMechanismHandler gssapiMechanismHandler = new GssapiMechanismHandler();
202             mechanismHandlerMap.put( SupportedSaslMechanisms.GSSAPI, gssapiMechanismHandler );
203 
204             NtlmMechanismHandler ntlmMechanismHandler = new NtlmMechanismHandler();
205             mechanismHandlerMap.put( SupportedSaslMechanisms.NTLM, ntlmMechanismHandler );
206             mechanismHandlerMap.put( SupportedSaslMechanisms.GSS_SPNEGO, ntlmMechanismHandler );
207 
208             ldapService.setSaslMechanismHandlers( mechanismHandlerMap );
209 
210             return ldapService;
211         }
212     }
213     
214 
215     /**
216      * Tests to make sure a negated search for OU of "test1" returns
217      * those entries that do not have the OU attribute or do not have
218      * a "test1" value for OU if the attribute exists.
219      */
220     @Test
221     public void testSearchNotOUIndexed() throws Exception
222     {
223         Set<SearchResult> results = getResults( "(!(ou=test1))" );
224         assertFalse( contains( "uid=test1,ou=test,ou=system", results ) );
225         assertTrue( contains( "uid=test2,ou=test,ou=system", results ) );
226         assertTrue( contains( "uid=testNoOU,ou=test,ou=system", results ) );
227     }
228 
229     
230     /**
231      * Tests to make sure a negated search for actors without ou
232      * with value 'drama' returns those that do not have the attribute
233      * and do not have a 'drama' value for ou if the attribute still
234      * exists.  This test DOES build an index on ou for the system
235      * partition and should have failed if the bug in DIRSERVER-951
236      * was present and reproducable.
237      */
238     @Test
239     public void testSearchNotDramaIndexed() throws Exception
240     {
241         // jack black has ou but not drama, and joe newbie has no ou what so ever
242         Set<SearchResult> results = getActorResults( "(!(ou=drama))" );
243         assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
244         assertTrue( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
245         assertEquals( 2, results.size() );
246     }
247 
248     
249     boolean contains( String dn, Set<SearchResult> results )
250     {
251         for ( SearchResult result : results )
252         {
253             if ( result.getNameInNamespace().equals( dn ) )
254             {
255                 return true;
256             }
257         }
258         
259         return false;
260     }
261     
262     
263     /**
264      * Tests to make sure a negated search for actors without ou
265      * with value 'drama' returns those that do not have the attribute
266      * and do not have a 'drama' value for ou if the attribute still
267      * exists.  This test DOES build an index on ou for the system
268      * partition and should have failed if the bug in DIRSERVER-951
269      * was present and reproducable.
270      */
271     @Test
272     public void testSearchNotDramaNotNewbieIndexed() throws Exception
273     {
274         // jack black has ou but not drama, and joe newbie has no ou what so ever
275         Set<SearchResult> results = getActorResults( "(& (!(uid=jnewbie)) (!(ou=drama)) )" );
276         assertTrue( contains( "uid=jblack,ou=actors,ou=system", results ) );
277         assertFalse( contains( "uid=jnewbie,ou=actors,ou=system", results ) );
278         assertEquals( 1, results.size() );
279     }
280 
281     
282     Set<SearchResult> getActorResults( String filter ) throws Exception
283     {
284         DirContext ctx = getWiredContext( ldapService );
285         Set<SearchResult> results = new HashSet<SearchResult>();
286         SearchControls controls = new SearchControls();
287         controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
288         NamingEnumeration<SearchResult> namingEnumeration = ctx.search( "ou=actors,ou=system", filter, controls );
289         while( namingEnumeration.hasMore() )
290         {
291             results.add( namingEnumeration.next() );
292         }
293         
294         return results;
295     }
296 
297     
298     Set<SearchResult> getResults( String filter ) throws Exception
299     {
300         DirContext ctx = getWiredContext( ldapService );
301         Set<SearchResult> results = new HashSet<SearchResult>();
302         SearchControls controls = new SearchControls();
303         controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
304         NamingEnumeration<SearchResult> namingEnumeration = ctx.search( "ou=system", filter, controls );
305         while( namingEnumeration.hasMore() )
306         {
307             results.add( namingEnumeration.next() );
308         }
309         
310         return results;
311     }
312 }