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 javax.naming.NamingEnumeration;
24  import javax.naming.directory.Attributes;
25  import javax.naming.directory.BasicAttributes;
26  import javax.naming.directory.DirContext;
27  import javax.naming.directory.SearchControls;
28  import javax.naming.directory.SearchResult;
29  
30  import org.apache.directory.server.core.integ.Level;
31  import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
32  import org.apache.directory.server.core.integ.annotations.CleanupLevel;
33  import org.apache.directory.server.integ.SiRunner;
34  import org.apache.directory.server.ldap.LdapService;
35  import org.junit.Test;
36  import org.junit.runner.RunWith;
37  
38  import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
39  
40  import static org.junit.Assert.fail;
41  import static org.junit.Assert.assertTrue;
42  import static org.junit.Assert.assertFalse;
43  import static org.junit.Assert.assertEquals;
44  import static org.junit.Assert.assertNotNull;
45  
46  
47  /**
48   * Test case with different search operations on the cn=schema entry. 
49   * Created to demonstrate DIRSERVER-1055
50   * 
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   * @version $Rev: 569048 $
53   */
54  @RunWith ( SiRunner.class ) 
55  @CleanupLevel ( Level.SUITE )
56  @ApplyLdifs( {
57      
58      // Bogus AD schema (not real)
59      
60      "dn: cn=active-directory, ou=schema\n" +
61      "objectclass: metaSchema\n" +
62      "objectclass: top\n" +
63      "cn: active-directory\n" +
64      "m-dependencies: core\n\n" +
65      
66      "dn: ou=attributeTypes, cn=active-directory, ou=schema\n" +
67      "objectclass: organizationalUnit\n" +
68      "objectclass: top\n" +
69      "ou: attributeTypes\n\n" +
70      
71      "dn: m-oid=1.1, ou=attributeTypes, cn=active-directory, ou=schema\n" +
72      "objectclass: metaAttributeType\n" +
73      "objectclass: metaTop\n" +
74      "objectclass: top\n" +
75      "m-oid: 1.1\n" +
76      "m-name: sAMAccountName\n" +
77      "m-syntax: 1.3.6.1.4.1.1466.115.121.1.15\n\n" +
78      
79      "dn: m-oid=1.2, ou=attributeTypes, cn=active-directory, ou=schema\n" +
80      "objectclass: metaAttributeType\n" +
81      "objectclass: metaTop\n" +
82      "objectclass: top\n" +
83      "m-oid: 1.2\n" +
84      "m-name: pwdLastSet\n" +
85      "m-equality: integerMatch\n" +
86      "m-ordering: integerMatch\n" +
87      "m-syntax: 1.3.6.1.4.1.1466.115.121.1.27\n\n" +
88  
89      "dn: m-oid=1.4, ou=attributeTypes, cn=active-directory, ou=schema\n" +
90      "objectclass: metaAttributeType\n" +
91      "objectclass: metaTop\n" +
92      "objectclass: top\n" +
93      "m-oid: 1.4\n" +
94      "m-name: useraccountcontrol\n" +
95      "m-syntax: 1.3.6.1.4.1.1466.115.121.1.27\n\n" +
96  
97      "dn: m-oid=1.5, ou=attributeTypes, cn=active-directory, ou=schema\n" +
98      "objectclass: metaAttributeType\n" +
99      "objectclass: metaTop\n" +
100     "objectclass: top\n" +
101     "m-oid: 1.5\n" +
102     "m-name: SourceAD\n" +
103     "m-syntax: 1.3.6.1.4.1.1466.115.121.1.15\n" +
104     "m-length: 0\n\n" +
105 
106     "dn: ou=comparators, cn=active-directory, ou=schema\n" +
107     "objectclass: organizationalUnit\n" +
108     "objectclass: top\n" +
109     "ou: comparators\n\n" +
110 
111     "dn: ou=ditContentRules, cn=active-directory, ou=schema\n" +
112     "objectclass: organizationalUnit\n" +
113     "objectclass: top\n" +
114     "ou: ditContentRules\n\n" +
115 
116     "dn: ou=ditStructureRules, cn=active-directory, ou=schema\n" +
117     "objectclass: organizationalUnit\n" +
118     "objectclass: top\n" +
119     "ou: ditStructureRules\n\n" +
120 
121     "dn: ou=matchingRules, cn=active-directory, ou=schema\n" +
122     "objectclass: organizationalUnit\n" +
123     "objectclass: top\n" +
124     "ou: matchingRules\n\n" +
125     
126     "dn: ou=nameForms, cn=active-directory, ou=schema\n" +
127     "objectclass: organizationalUnit\n" +
128     "objectclass: top\n" +
129     "ou: nameForms\n\n" +
130 
131     "dn: ou=normalizers, cn=active-directory, ou=schema\n" +
132     "objectclass: organizationalUnit\n" +
133     "objectclass: top\n" +
134     "ou: normalizers\n\n" +
135 
136     "dn: ou=objectClasses, cn=active-directory, ou=schema\n" +
137     "objectclass: organizationalUnit\n" +
138     "objectclass: top\n" +
139     "ou: objectClasses\n\n" +
140 
141     "dn: m-oid=1.3, ou=objectClasses, cn=active-directory, ou=schema\n" +
142     "objectclass: metaObjectClass\n" +
143     "objectclass: metaTop\n" +
144     "objectclass: top\n" +
145     "m-oid: 1.3\n" +
146     "m-name: personActiveDirectory\n" +
147     "m-supObjectClass: person\n" +
148     "m-must: pwdLastSet\n" +
149     "m-must: sAMAccountName\n" +
150     "m-must: useraccountcontrol\n" +
151     "m-must: SourceAD\n\n" +
152 
153     "dn: ou=syntaxCheckers, cn=active-directory, ou=schema\n" +
154     "objectclass: organizationalUnit\n" +
155     "objectclass: top\n" +
156     "ou: syntaxCheckers\n\n" +
157 
158     "dn: ou=syntaxes, cn=active-directory, ou=schema\n" +
159     "objectclass: organizationalUnit\n" +
160     "objectclass: top\n" +
161     "ou: syntaxes\n\n"
162     }
163 )
164 public class SchemaSearchIT 
165 {
166     private static final String DN = "cn=schema";
167     private static final String FILTER = "(objectclass=subschema)";
168 
169     
170     public static LdapService ldapService;
171     
172 
173     protected void checkForAttributes( Attributes attrs, String[] attrNames )
174     {
175         for ( int i = 0; i < attrNames.length; i++ )
176         {
177             String attrName = attrNames[i];
178 
179             assertNotNull( "Check if attr " + attrName + " is present", attrs.get( attrNames[i] ) );
180         }
181     }
182 
183 
184     /**
185      * Check if modifyTimestamp and createTimestamp are present in the search result,
186      * if they are requested.
187      */
188     @Test
189     public void testRequestOperationalAttributes() throws Exception
190     {
191         DirContext ctx = getWiredContext( ldapService );
192         SearchControls ctls = new SearchControls();
193 
194         String[] attrNames =
195             { "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" };
196 
197         ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
198         ctls.setReturningAttributes( attrNames );
199 
200         NamingEnumeration<SearchResult> result = ctx.search( DN, FILTER, ctls );
201 
202         if ( result.hasMore() )
203         {
204             SearchResult entry = result.next();
205             checkForAttributes( entry.getAttributes(), attrNames );
206         }
207         else
208         {
209             fail( "entry " + DN + " not found" );
210         }
211 
212         result.close();
213     }
214 
215 
216     /**
217      * Check if modifyTimestamp and createTimestamp are present in the search result,
218      * if + is requested.
219      */
220     @Test
221     public void testRequestAllOperationalAttributes() throws Exception
222     {
223         DirContext ctx = getWiredContext( ldapService );
224         SearchControls ctls = new SearchControls();
225 
226         ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
227         ctls.setReturningAttributes( new String[]
228             { "+" } );
229 
230         NamingEnumeration<SearchResult> result = ctx.search( DN, FILTER, ctls );
231 
232         if ( result.hasMore() )
233         {
234             SearchResult entry = result.next();
235             String[] attrNames =
236                 { "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" };
237             checkForAttributes( entry.getAttributes(), attrNames );
238         }
239         else
240         {
241             fail( "entry " + DN + " not found" );
242         }
243 
244         result.close();
245     }
246 
247     
248     /**
249      * Test case for DIRSERVER-1083: Search on an custom attribute added to 
250      * the dynamic schema fails when no result is found. 
251      */
252     @Test
253     public void testSearchingNewSchemaElements() throws Exception
254     {
255         DirContext ctx = getWiredContext( ldapService );
256         
257         // create an entry with the schema objectClass personActiveDirectory
258         Attributes person = new BasicAttributes( "objectClass", "top", true );
259         person.get( "objectClass" ).add( "person" );
260         person.get( "objectClass" ).add( "personActiveDirectory" );
261         person.put( "cn", "foobar" );
262         person.put( "sn", "bar" );
263         person.put( "pwdLastSet", "3" );
264         person.put( "SourceAD", "blah" );
265         person.put( "useraccountcontrol", "7" );
266         person.put( "sAMAccountName", "foobar" );
267         ctx.createSubcontext( "cn=foobar,ou=system", person );
268         
269         // Confirm creation with a lookup
270         Attributes read = ctx.getAttributes( "cn=foobar,ou=system" );
271         assertNotNull( read );
272         assertEquals( "3", read.get( "pwdLastSet" ).get() );
273         
274         // Now search for foobar with pwdLastSet value of 3
275         SearchControls searchControls = new SearchControls();
276         searchControls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
277         NamingEnumeration<SearchResult> results = ctx.search( "ou=system", "(pwdLastSet=3)", searchControls );
278         assertTrue( results.hasMore() );
279         SearchResult result = results.next();
280         assertNotNull( result );
281         assertEquals( "cn=foobar", result.getName() );
282         Attributes attributes = result.getAttributes();
283         assertEquals( "3", attributes.get( "pwdLastSet" ).get() );
284         results.close();
285         
286         // Now search with bogus value for pwdLastSet
287         results = ctx.search( "ou=system", "(pwdLastSet=300)", searchControls );
288         assertFalse( results.hasMore() );
289         results.close();
290     }
291     
292     
293     /**
294      * Test case for DIRSERVER-: Ensure that schema entry is returned, 
295      * even if no ManageDsaIT control is present in the search request.
296      */
297     @Test
298     public void testRequestWithoutManageDsaITControl() throws Exception
299     {
300         DirContext ctx = getWiredContext( ldapService );
301 
302         // this removes the ManageDsaIT control from the search request
303         ctx.addToEnvironment( DirContext.REFERRAL, "throw" );
304 
305         SearchControls ctls = new SearchControls();
306         String[] attrNames =
307             { "objectClasses", "attributeTypes", "ldapSyntaxes", "matchingRules", "matchingRuleUse", "createTimestamp",
308                 "modifyTimestamp" };
309         ctls.setSearchScope( SearchControls.OBJECT_SCOPE );
310         ctls.setReturningAttributes( attrNames );
311 
312         NamingEnumeration<SearchResult> result = ctx.search( DN, FILTER, ctls );
313 
314         if ( result.hasMore() )
315         {
316             SearchResult entry = result.next();
317             checkForAttributes( entry.getAttributes(), attrNames );
318         }
319         else
320         {
321             fail( "entry " + DN + " not found" );
322         }
323 
324         result.close();
325     }
326 }