1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.schema;
18  
19  
20  import junit.framework.TestCase;
21  
22  import javax.naming.directory.*;
23  import javax.naming.Name;
24  import javax.naming.NamingException;
25  
26  import org.apache.ldap.common.name.LdapName;
27  import org.apache.ldap.common.exception.LdapSchemaViolationException;
28  import org.apache.ldap.common.message.ResultCodeEnum;
29  import org.apache.ldap.server.schema.bootstrap.*;
30  
31  import java.util.Set;
32  import java.util.HashSet;
33  
34  
35  /***
36   * Tests to make sure the schema checker is operating correctly.
37   *
38   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
39   * @version $Rev$, $Date$
40   */
41  public class SchemaCheckerTest extends TestCase
42  {
43      GlobalRegistries registries = null;
44  
45  
46      public SchemaCheckerTest() throws NamingException
47      {
48          this( "SchemaCheckerTest" );
49      }
50  
51  
52      public SchemaCheckerTest( String name ) throws NamingException
53      {
54          super( name );
55  
56          BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
57  
58          BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
59          Set schemas = new HashSet();
60          schemas.add( new SystemSchema() );
61          schemas.add( new ApacheSchema() );
62          schemas.add( new CoreSchema() );
63          schemas.add( new CosineSchema() );
64          schemas.add( new InetorgpersonSchema() );
65          schemas.add( new JavaSchema() );
66          loader.load( schemas, bootstrapRegistries );
67  
68          java.util.List errors = bootstrapRegistries.checkRefInteg();
69          if ( !errors.isEmpty() )
70          {
71              NamingException e = new NamingException();
72              e.setRootCause( ( Throwable ) errors.get( 0 ) );
73              throw e;
74          }
75  
76          registries = new GlobalRegistries( bootstrapRegistries );
77      }
78  
79  
80      private GlobalRegistries getGlobalRegistries() throws NamingException
81      {
82          BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
83  
84          BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
85          Set schemas = new HashSet();
86          schemas.add( new SystemSchema() );
87          schemas.add( new ApacheSchema() );
88          schemas.add( new CoreSchema() );
89          schemas.add( new CosineSchema() );
90          schemas.add( new InetorgpersonSchema() );
91          schemas.add( new JavaSchema() );
92          loader.load( schemas, bootstrapRegistries );
93  
94          java.util.List errors = bootstrapRegistries.checkRefInteg();
95          if ( !errors.isEmpty() )
96          {
97              NamingException e = new NamingException();
98              e.setRootCause( ( Throwable ) errors.get( 0 ) );
99              throw e;
100         }
101 
102         return new GlobalRegistries( bootstrapRegistries );
103     }
104 
105 
106     /***
107      * Test case to check the schema checker operates correctly when modify
108      * operations replace objectClasses.
109      */
110     public void testPreventStructuralClassRemovalOnModifyReplace() throws Exception
111     {
112         Name name = new LdapName( "uid=akarasulu,ou=users,dc=example,dc=com" );
113         int mod = DirContext.REPLACE_ATTRIBUTE;
114         Attributes modifyAttributes = new BasicAttributes( true );
115         modifyAttributes.put( new BasicAttribute( "cn" ) );
116 
117         ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
118 
119         // this should pass
120         SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
121 
122         // this should succeed since person is still in replaced set and is structural
123         modifyAttributes.remove( "cn" );
124         BasicAttribute objectClassesReplaced = new BasicAttribute( "objectClass" );
125         objectClassesReplaced.add( "top" );
126         objectClassesReplaced.add( "person" );
127         modifyAttributes.put( objectClassesReplaced );
128         SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
129 
130         // this should fail since only top is left
131         objectClassesReplaced = new BasicAttribute( "objectClass" );
132         objectClassesReplaced.add( "top" );
133         modifyAttributes.put( objectClassesReplaced );
134         try
135         {
136             SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
137             fail( "should never get here due to an LdapSchemaViolationException" );
138         }
139         catch ( LdapSchemaViolationException e )
140         {
141             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
142         }
143 
144         // this should fail since the modify operation tries to delete all
145         // objectClass attribute values
146         modifyAttributes.remove( "cn" );
147         objectClassesReplaced = new BasicAttribute( "objectClass" );
148         modifyAttributes.put( objectClassesReplaced );
149         try
150         {
151             SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, modifyAttributes );
152             fail( "should never get here due to an LdapSchemaViolationException" );
153         }
154         catch ( LdapSchemaViolationException e )
155         {
156             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
157         }
158     }
159 
160 
161     /***
162      * Test case to check the schema checker operates correctly when modify
163      * operations remove objectClasses.
164      */
165     public void testPreventStructuralClassRemovalOnModifyRemove() throws Exception
166     {
167         Name name = new LdapName( "uid=akarasulu,ou=users,dc=example,dc=com" );
168         int mod = DirContext.REMOVE_ATTRIBUTE;
169         Attributes modifyAttributes = new BasicAttributes( true );
170         Attribute entryObjectClasses = new BasicAttribute( "objectClass" );
171         entryObjectClasses.add( "top" );
172         entryObjectClasses.add( "person" );
173         entryObjectClasses.add( "organizationalPerson" );
174         modifyAttributes.put( new BasicAttribute( "cn" ) );
175 
176         ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
177 
178         // this should pass
179         SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
180                 entryObjectClasses );
181 
182         // this should succeed since person is left and is structural
183         modifyAttributes.remove( "cn" );
184         BasicAttribute objectClassesRemoved = new BasicAttribute( "objectClass" );
185         objectClassesRemoved.add( "person" );
186         modifyAttributes.put( objectClassesRemoved );
187         SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
188                 entryObjectClasses );
189 
190         // this should fail since only top is left
191         modifyAttributes.remove( "cn" );
192         objectClassesRemoved = new BasicAttribute( "objectClass" );
193         objectClassesRemoved.add( "person" );
194         objectClassesRemoved.add( "organizationalPerson" );
195         modifyAttributes.put( objectClassesRemoved );
196         try
197         {
198             SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
199                     entryObjectClasses );
200             fail( "should never get here due to an LdapSchemaViolationException" );
201         }
202         catch ( LdapSchemaViolationException e )
203         {
204             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
205         }
206 
207         // this should fail since the modify operation tries to delete all
208         // objectClass attribute values
209         modifyAttributes.remove( "cn" );
210         objectClassesRemoved = new BasicAttribute( "objectClass" );
211         modifyAttributes.put( objectClassesRemoved );
212         try
213         {
214             SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, modifyAttributes,
215                     entryObjectClasses );
216             fail( "should never get here due to an LdapSchemaViolationException" );
217         }
218         catch ( LdapSchemaViolationException e )
219         {
220             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
221         }
222     }
223 
224 
225     /***
226      * Test case to check the schema checker operates correctly when modify
227      * operations remove RDN attributes.
228      */
229     public void testPreventRdnChangeOnModifyRemove() throws Exception
230     {
231         int mod = DirContext.REMOVE_ATTRIBUTE;
232         Name name = new LdapName( "ou=user,dc=example,dc=com" );
233         Attributes attributes = new BasicAttributes( true );
234         attributes.put( "cn", "does not matter" );
235 
236         // postive test which should pass
237         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
238 
239         // test should fail since we are removing the ou attribute
240         attributes.put( new BasicAttribute( "ou" ) );
241         try
242         {
243             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
244             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
245         }
246         catch ( LdapSchemaViolationException e )
247         {
248             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
249         }
250 
251         // test success using more than one attribute for the Rdn but not modifying rdn attribute
252         name = new LdapName( "ou=users+cn=system users,dc=example,dc=com" );
253         attributes = new BasicAttributes( true );
254         attributes.put( "sn", "does not matter" );
255         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
256 
257         // test for failure when modifying Rdn attribute in multi attribute Rdn
258         attributes.put( new BasicAttribute( "cn" ) );
259         try
260         {
261             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
262             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
263         }
264         catch ( LdapSchemaViolationException e )
265         {
266             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
267         }
268 
269         // should succeed since the value being deleted from the rdn attribute is
270         // is not used when composing the Rdn
271         attributes = new BasicAttributes( true );
272         attributes.put( "ou", "container" );
273         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
274 
275         // now let's make it fail again just by providing the right value for ou (users)
276         attributes = new BasicAttributes( true );
277         attributes.put( "ou", "users" );
278         try
279         {
280             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, attributes );
281             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
282         }
283         catch ( LdapSchemaViolationException e )
284         {
285             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
286         }
287     }
288 
289 
290     /***
291      * Test case to check the schema checker operates correctly when modify
292      * operations replace RDN attributes.
293      */
294     public void testPreventRdnChangeOnModifyReplace() throws Exception
295     {
296         int mod = DirContext.REPLACE_ATTRIBUTE;
297         Name name = new LdapName( "ou=user,dc=example,dc=com" );
298         Attributes attributes = new BasicAttributes( true );
299         attributes.put( "cn", "does not matter" );
300 
301         // postive test which should pass
302         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
303 
304         // test should fail since we are removing the ou attribute
305         attributes.put( new BasicAttribute( "ou" ) );
306         try
307         {
308             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
309             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
310         }
311         catch ( LdapSchemaViolationException e )
312         {
313             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
314         }
315 
316         // test success using more than one attribute for the Rdn but not modifying rdn attribute
317         name = new LdapName( "ou=users+cn=system users,dc=example,dc=com" );
318         attributes = new BasicAttributes( true );
319         attributes.put( "sn", "does not matter" );
320         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
321 
322         // test for failure when modifying Rdn attribute in multi attribute Rdn
323         attributes.put( new BasicAttribute( "cn" ) );
324         try
325         {
326             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
327             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
328         }
329         catch ( LdapSchemaViolationException e )
330         {
331             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
332         }
333 
334         // should succeed since the values being replaced from the rdn attribute is
335         // is includes the old Rdn attribute value
336         attributes = new BasicAttributes( true );
337         attributes.put( "ou", "container" );
338         attributes.put( "ou", "users" );
339         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
340 
341         // now let's make it fail by not including the old value for ou (users)
342         attributes = new BasicAttributes( true );
343         attributes.put( "ou", "container" );
344         try
345         {
346             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attributes );
347             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
348         }
349         catch ( LdapSchemaViolationException e )
350         {
351             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
352         }
353     }
354 
355 
356     // ------------------------------------------------------------------------
357     // Single Attribute Test Cases
358     // ------------------------------------------------------------------------
359 
360 
361     /***
362      * Test case to check the schema checker operates correctly when modify
363      * operations replace objectClasses.
364      */
365     public void testPreventStructuralClassRemovalOnModifyReplaceAttribute() throws Exception
366     {
367         ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
368 
369         // this should pass
370         Name name = new LdapName( "uid=akarasulu,ou=users,dc=example,dc=com" );
371         int mod = DirContext.REPLACE_ATTRIBUTE;
372         SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, new BasicAttribute( "cn" ) );
373 
374         // this should succeed since person is still in replaced set and is structural
375         BasicAttribute objectClassesReplaced = new BasicAttribute( "objectClass" );
376         objectClassesReplaced.add( "top" );
377         objectClassesReplaced.add( "person" );
378         SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
379 
380         // this should fail since only top is left
381         objectClassesReplaced = new BasicAttribute( "objectClass" );
382         objectClassesReplaced.add( "top" );
383         try
384         {
385             SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
386             fail( "should never get here due to an LdapSchemaViolationException" );
387         }
388         catch ( LdapSchemaViolationException e )
389         {
390             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
391         }
392 
393         // this should fail since the modify operation tries to delete all
394         // objectClass attribute values
395         objectClassesReplaced = new BasicAttribute( "objectClass" );
396         try
397         {
398             SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, mod, objectClassesReplaced );
399             fail( "should never get here due to an LdapSchemaViolationException" );
400         }
401         catch ( LdapSchemaViolationException e )
402         {
403             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
404         }
405     }
406 
407 
408     /***
409      * Test case to check the schema checker operates correctly when modify
410      * operations remove objectClasses.
411      */
412     public void testPreventStructuralClassRemovalOnModifyRemoveAttribute() throws Exception
413     {
414         Name name = new LdapName( "uid=akarasulu,ou=users,dc=example,dc=com" );
415         int mod = DirContext.REMOVE_ATTRIBUTE;
416         Attribute entryObjectClasses = new BasicAttribute( "objectClass" );
417         entryObjectClasses.add( "top" );
418         entryObjectClasses.add( "person" );
419         entryObjectClasses.add( "organizationalPerson" );
420 
421         ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
422 
423         // this should pass
424         SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, new BasicAttribute( "cn" ),
425                 entryObjectClasses );
426 
427         // this should succeed since person is left and is structural
428         BasicAttribute objectClassesRemoved = new BasicAttribute( "objectClass" );
429         objectClassesRemoved.add( "person" );
430         SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
431                 entryObjectClasses );
432 
433         // this should fail since only top is left
434         objectClassesRemoved = new BasicAttribute( "objectClass" );
435         objectClassesRemoved.add( "person" );
436         objectClassesRemoved.add( "organizationalPerson" );
437         try
438         {
439             SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
440                     entryObjectClasses );
441             fail( "should never get here due to an LdapSchemaViolationException" );
442         }
443         catch ( LdapSchemaViolationException e )
444         {
445             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
446         }
447 
448         // this should fail since the modify operation tries to delete all
449         // objectClass attribute values
450         objectClassesRemoved = new BasicAttribute( "objectClass" );
451         try
452         {
453             SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, mod, objectClassesRemoved,
454                     entryObjectClasses );
455             fail( "should never get here due to an LdapSchemaViolationException" );
456         }
457         catch ( LdapSchemaViolationException e )
458         {
459             assertEquals( e.getResultCode(), ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
460         }
461     }
462 
463 
464     /***
465      * Test case to check the schema checker operates correctly when modify
466      * operations remove RDN attributes.
467      */
468     public void testPreventRdnChangeOnModifyRemoveAttribute() throws Exception
469     {
470         int mod = DirContext.REMOVE_ATTRIBUTE;
471         Name name = new LdapName( "ou=user,dc=example,dc=com" );
472 
473         // postive test which should pass
474         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "cn", "does not matter" ) );
475 
476         // test should fail since we are removing the ou attribute
477         try
478         {
479             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "ou" ) );
480             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
481         }
482         catch ( LdapSchemaViolationException e )
483         {
484             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
485         }
486 
487         // test success using more than one attribute for the Rdn but not modifying rdn attribute
488         name = new LdapName( "ou=users+cn=system users,dc=example,dc=com" );
489         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "sn", "does not matter" ) );
490 
491         // test for failure when modifying Rdn attribute in multi attribute Rdn
492         try
493         {
494             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "cn" ) );
495             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
496         }
497         catch ( LdapSchemaViolationException e )
498         {
499             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
500         }
501 
502         // should succeed since the value being deleted from the rdn attribute is
503         // is not used when composing the Rdn
504         SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "ou", "container" ) );
505 
506         // now let's make it fail again just by providing the right value for ou (users)
507         try
508         {
509             SchemaChecker.preventRdnChangeOnModifyRemove( name, mod, new BasicAttribute( "ou", "users" ) );
510             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
511         }
512         catch ( LdapSchemaViolationException e )
513         {
514             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
515         }
516     }
517 
518 
519     /***
520      * Test case to check the schema checker operates correctly when modify
521      * operations replace RDN attributes.
522      */
523     public void testPreventRdnChangeOnModifyReplaceAttribute() throws Exception
524     {
525         int mod = DirContext.REPLACE_ATTRIBUTE;
526         Name name = new LdapName( "ou=user,dc=example,dc=com" );
527 
528         // postive test which should pass
529         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new BasicAttribute( "cn", "does not matter" ) );
530 
531         // test should fail since we are removing the ou attribute
532         try
533         {
534             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new BasicAttribute( "ou" ) );
535             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
536         }
537         catch ( LdapSchemaViolationException e )
538         {
539             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
540         }
541 
542         // test success using more than one attribute for the Rdn but not modifying rdn attribute
543         name = new LdapName( "ou=users+cn=system users,dc=example,dc=com" );
544         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new BasicAttribute( "sn", "does not matter" ) );
545 
546         // test for failure when modifying Rdn attribute in multi attribute Rdn
547         try
548         {
549             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, new BasicAttribute( "cn" ) );
550             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
551         }
552         catch ( LdapSchemaViolationException e )
553         {
554             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
555         }
556 
557         // should succeed since the values being replaced from the rdn attribute is
558         // is includes the old Rdn attribute value
559         Attribute attribute = new BasicAttribute( "ou" );
560         attribute.add( "container" );
561         attribute.add( "users" );
562         SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attribute );
563 
564         // now let's make it fail by not including the old value for ou (users)
565         attribute = new BasicAttribute( "ou" );
566         attribute.add( "container" );
567         try
568         {
569             SchemaChecker.preventRdnChangeOnModifyReplace( name, mod, attribute );
570             fail( "should never get here due to a LdapSchemaViolationException being thrown" );
571         }
572         catch ( LdapSchemaViolationException e )
573         {
574             assertEquals( ResultCodeEnum.NOTALLOWEDONRDN, e.getResultCode() );
575         }
576     }
577 }