View Javadoc

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.core.schema;
21  
22  
23  import org.apache.directory.server.core.entry.ServerAttribute;
24  import org.apache.directory.server.core.entry.ServerEntry;
25  import org.apache.directory.server.schema.registries.ObjectClassRegistry;
26  import org.apache.directory.server.schema.registries.OidRegistry;
27  import org.apache.directory.shared.ldap.constants.SchemaConstants;
28  import org.apache.directory.shared.ldap.entry.EntryAttribute;
29  import org.apache.directory.shared.ldap.entry.ModificationOperation;
30  import org.apache.directory.shared.ldap.entry.Value;
31  import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
32  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
33  import org.apache.directory.shared.ldap.name.LdapDN;
34  import org.apache.directory.shared.ldap.schema.AttributeType;
35  import org.apache.directory.shared.ldap.schema.ObjectClass;
36  import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
37  import org.apache.directory.shared.ldap.util.NamespaceTools;
38  
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import javax.naming.NamingException;
43  
44  import java.util.ArrayList;
45  import java.util.List;
46  import java.util.Set;
47  import java.util.HashSet;
48  
49  
50  /**
51   * Performs schema checks on behalf of the SchemaInterceptor.
52   *
53   * TODO: we really need to refactor this code since there's much duplication
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev: 691847 $, $Date: 2008-09-04 03:23:12 +0200 (Do, 04 Sep 2008) $
56   */
57  public class SchemaChecker
58  {
59      /** the SLF4J logger for this class */
60      private static Logger log = LoggerFactory.getLogger( SchemaChecker.class );
61  
62  
63      /**
64       * Makes sure modify operations do not leave the entry without a STRUCTURAL
65       * objectClass.  At least one STRUCTURAL objectClass must be specified for
66       * the entry after modifications take effect.
67       *
68       * @param registry the objectClass registry to lookup ObjectClass specifications
69       * @param name the name of the entry being modified
70       * @param mod the type of modification operation being performed (should be
71       * REMOVE_ATTRIBUTE)
72       * @param attribute the attribute being modified
73       * @throws NamingException if modify operations leave the entry inconsistent
74       * without a STRUCTURAL objectClass
75       */
76      public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, LdapDN name, ModificationOperation mod,
77          ServerAttribute attribute ) throws NamingException
78      {
79          if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
80          {
81              return;
82          }
83  
84          if ( !SchemaConstants.OBJECT_CLASS_AT.equalsIgnoreCase( attribute.getUpId() ) )
85          {
86              return;
87          }
88  
89          // whoever issued the modify operation is insane they want to delete
90          // all the objectClass values in which case we must throw an exception
91          if ( attribute.size() == 0 )
92          {
93              String msg = "Modify operation leaves no structural objectClass for entry " + name;
94              
95              if ( log.isInfoEnabled() )
96              {
97                  log.info( msg + ".  Raising LdapSchemaViolationException." );
98              }
99              
100             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
101         }
102 
103         // check that there is at least one structural objectClass in the replacement set
104         for ( Value<?> value:attribute )
105         {
106             ObjectClass ocType = registry.lookup( ( String ) value.get() );
107 
108             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
109             {
110                 return;
111             }
112         }
113 
114         // no structural object classes exist for the entry in the replacement
115         // set for the objectClass attribute so we need to complain about that
116         String msg = "Modify operation leaves no structural objectClass for entry " + name;
117         if ( log.isInfoEnabled() )
118         {
119             log.info( msg + ".  Raising LdapSchemaViolationException." );
120         }
121         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
122     }
123 
124 
125     /**
126      * Makes sure modify operations do not leave the entry without a STRUCTURAL
127      * objectClass.  At least one STRUCTURAL objectClass must be specified for
128      * the entry after modifications take effect.
129      *
130      * @param registry the objectClass registry to lookup ObjectClass specifications
131      * @param name the name of the entry being modified
132      * @param mod the type of modification operation being performed (should be
133      * REMOVE_ATTRIBUTE)
134      * @param entry the entry being modified
135      * @throws NamingException if modify operations leave the entry inconsistent
136      * without a STRUCTURAL objectClass
137      */
138     public static void preventStructuralClassRemovalOnModifyReplace( 
139         ObjectClassRegistry registry, LdapDN name, ModificationOperation mod, ServerEntry entry ) throws NamingException
140     {
141         if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
142         {
143             return;
144         }
145 
146         EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
147         
148         if ( objectClass == null )
149         {
150             return;
151         }
152 
153         // whoever issued the modify operation is insane they want to delete
154         // all the objectClass values in which case we must throw an exception
155         if ( objectClass.size() == 0 )
156         {
157             String msg = "Modify operation leaves no structural objectClass for entry " + name;
158             if ( log.isInfoEnabled() )
159             {
160                 log.info( msg + ".  Raising LdapSchemaViolationException." );
161             }
162             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
163         }
164 
165         // check that there is at least one structural objectClass in the replacement set
166         for ( Value<?> value:objectClass )
167         {
168             ObjectClass ocType = registry.lookup( ( String ) value.get() );
169             
170             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
171             {
172                 return;
173             }
174         }
175 
176         // no structural object classes exist for the entry in the replacement
177         // set for the objectClass attribute so we need to complain about that
178         String msg = "Modify operation leaves no structural objectClass for entry " + name;
179         if ( log.isInfoEnabled() )
180         {
181             log.info( msg + ".  Raising LdapSchemaViolationException." );
182         }
183         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
184     }
185 
186 
187     /**
188      * Makes sure modify operations do not leave the entry without a STRUCTURAL
189      * objectClass.  At least one STRUCTURAL objectClass must be specified for
190      * the entry after modifications take effect.
191      *
192      * @param registry the objectClass registry to lookup ObjectClass specifications
193      * @param name the name of the entry being modified
194      * @param mod the type of modification operation being performed (should be
195      * REMOVE_ATTRIBUTE)
196      * @param attribute the attribute being modified
197      * @param entryObjectClasses the entry being modified
198      * @throws NamingException if modify operations leave the entry inconsistent
199      * without a STRUCTURAL objectClass
200      */
201     public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, LdapDN name, ModificationOperation mod,
202         EntryAttribute attribute, EntryAttribute entryObjectClasses ) throws NamingException
203     {
204         if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
205         {
206             return;
207         }
208 
209         if ( !((ServerAttribute)attribute).instanceOf( SchemaConstants.OBJECT_CLASS_AT ) )
210         {
211             return;
212         }
213         
214         // check if there is any attribute value as "".
215         // if there is remove it so that it will be considered as not even provided.
216         List<Value<?>> removed = new ArrayList<Value<?>>();
217         
218         // Fist gather the value to remove
219         for ( Value<?> value:attribute )
220         {
221             if ( ((String)value.get()).length() == 0 )
222             {
223                 removed.add( value );
224             }
225         }
226         
227         // Now remove the values from the attribute
228         for ( Value<?> value:removed )
229         {
230             attribute.remove( value );
231         }
232 
233         // whoever issued the modify operation is insane they want to delete
234         // all the objectClass values in which case we must throw an exception
235         if ( attribute.size() == 0 )
236         {
237             String msg = "Modify operation leaves no structural objectClass for entry " + name;
238             
239             if ( log.isInfoEnabled() )
240             {
241                 log.info( msg + ".  Raising LdapSchemaViolationException." );
242             }
243             
244             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
245         }
246 
247         // remove all the objectClass attribute values from a cloned copy and then
248         // we can analyze what remains in this attribute to make sure a structural
249         // objectClass is present for the entry
250 
251         ServerAttribute cloned = ( ServerAttribute ) entryObjectClasses.clone();
252         
253         for ( Value<?> value:attribute )
254         {
255             cloned.remove( value );
256         }
257 
258         // check resultant set of objectClass values for a structural objectClass
259         for ( Value<?> objectClass:cloned )
260         {
261             ObjectClass oc = registry.lookup( (String)objectClass.get() );
262             
263             if ( oc.getType() == ObjectClassTypeEnum.STRUCTURAL )
264             {
265                 return;
266             }
267         }
268 
269         // no structural object classes exist for the entry after the modifications
270         // to the objectClass attribute so we need to complain about that
271         String msg = "Modify operation leaves no structural objectClass for entry " + name;
272 
273         if ( log.isInfoEnabled() )
274         {
275             log.info( msg + ".  Raising LdapSchemaViolationException." );
276         }
277         
278         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
279     }
280 
281 
282     /**
283      * Makes sure modify operations do not leave the entry without a STRUCTURAL
284      * objectClass.  At least one STRUCTURAL objectClass must be specified for
285      * the entry after modifications take effect.
286      *
287      * @param registry the objectClass registry to lookup ObjectClass specifications
288      * @param name the name of the entry being modified
289      * @param mod the type of modification operation being performed (should be
290      * REMOVE_ATTRIBUTE)
291      * @param attributes the attributes being modified
292      * @param entryObjectClasses the entry being modified
293      * @throws NamingException if modify operations leave the entry inconsistent
294      * without a STRUCTURAL objectClass
295      *
296     public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
297         Attributes attributes, Attribute entryObjectClasses ) throws NamingException
298     {
299         if ( mod != DirContext.REMOVE_ATTRIBUTE )
300         {
301             return;
302         }
303 
304         Attribute objectClass = attributes.get( SchemaConstants.OBJECT_CLASS_AT );
305         if ( objectClass == null )
306         {
307             return;
308         }
309         
310         // check if there is any attribute value as "".
311         // if there is remove it so that it will be considered as not even provided.
312         for( int ii = 0; ii < objectClass.size(); ii++ )
313         {
314             Object value = objectClass.get( ii );
315             if ( "".equals( value ) )
316             {
317                 objectClass.remove( ii );
318             }
319         }
320 
321         // whoever issued the modify operation is insane they want to delete
322         // all the objectClass values in which case we must throw an exception
323         if ( objectClass.size() == 0 )
324         {
325             String msg = "Modify operation leaves no structural objectClass for entry " + name;
326             if ( log.isInfoEnabled() )
327             {
328                 log.info( msg + ".  Raising LdapSchemaViolationException." );
329             }
330             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
331         }
332 
333         // remove all the objectClass attribute values from a cloned copy and then
334         // we can analyze what remains in this attribute to make sure a structural
335         // objectClass is present for the entry
336 
337         Attribute cloned = ( Attribute ) entryObjectClasses.clone();
338         for ( int ii = 0; ii < objectClass.size(); ii++ )
339         {
340             cloned.remove( objectClass.get( ii ) );
341         }
342 
343         // check resultant set of objectClass values for a structural objectClass
344         for ( int ii = 0; ii < cloned.size(); ii++ )
345         {
346             ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
347             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
348             {
349                 return;
350             }
351         }
352 
353         // no structural object classes exist for the entry after the modifications
354         // to the objectClass attribute so we need to complain about that
355         String msg = "Modify operation leaves no structural objectClass for entry " + name;
356         if ( log.isInfoEnabled() )
357         {
358             log.info( msg + ".  Raising LdapSchemaViolationException." );
359         }
360         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
361     }
362     */
363 
364     /**
365      * Makes sure a modify operation does not replace RDN attributes or their value.
366      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
367      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
368      * seen below:
369      * <p/>
370      * <pre>
371      *     The Modify Operation cannot be used to remove from an entry any of
372      *     its distinguished values, those values which form the entry's
373      *     relative distinguished name.  An attempt to do so will result in the
374      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
375      *     described in section 4.9 is used to rename an entry.
376      * </pre>
377      *
378      * @param name the distinguished name of the attribute being modified
379      * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
380      * @param attribute the attribute being modified
381      * @param oidRegistry
382      * @throws NamingException if the modify operation is removing an Rdn attribute
383      */
384     public static void preventRdnChangeOnModifyReplace( LdapDN name, ModificationOperation mod, ServerAttribute attribute, OidRegistry oidRegistry )
385         throws NamingException
386     {
387         if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
388         {
389             return;
390         }
391 
392         Set<String> rdnAttributes = getRdnAttributes( name );
393         String id = oidRegistry.getOid( attribute.getUpId() );
394 
395         if ( !rdnAttributes.contains( id ) )
396         {
397             return;
398         }
399 
400         // if the attribute values to delete are not specified then all values
401         // for the attribute are to be deleted in which case we must just throw
402         // a schema violation exception with the notAllowedOnRdn result code
403         if ( attribute.size() == 0 )
404         {
405             String msg = "Modify operation attempts to delete RDN attribute ";
406             msg += id + " on entry " + name + " violates schema constraints";
407 
408             if ( log.isInfoEnabled() )
409             {
410                 log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
411             }
412             throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
413         }
414 
415         // from here on the modify operation replaces specific values
416         // of the Rdn attribute so we must check to make sure all the old
417         // rdn attribute values are present in the replacement set
418         String rdnValue = getRdnValue( id, name, oidRegistry );
419         for ( int ii = 0; ii < attribute.size(); ii++ )
420         {
421             // if the old rdn value is not in the rdn attribute then
422             // we must complain with a schema violation
423             if ( !attribute.contains( rdnValue ) )
424             {
425                 String msg = "Modify operation attempts to delete RDN attribute values in use for ";
426                 msg += id + " on entry " + name + " and violates schema constraints";
427 
428                 if ( log.isInfoEnabled() )
429                 {
430                     log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
431                 }
432                 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
433             }
434         }
435     }
436 
437 
438     /**
439      * Makes sure a modify operation does not replace RDN attributes or their value.
440      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
441      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
442      * seen below:
443      * <p/>
444      * <pre>
445      *     The Modify Operation cannot be used to remove from an entry any of
446      *     its distinguished values, those values which form the entry's
447      *     relative distinguished name.  An attempt to do so will result in the
448      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
449      *     described in section 4.9 is used to rename an entry.
450      * </pre>
451      *
452      * @param name the distinguished name of the attribute being modified
453      * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
454      * @param entry
455      * @param oidRegistry
456      * @throws NamingException if the modify operation is removing an Rdn attribute
457      */
458     public static void preventRdnChangeOnModifyReplace( 
459         LdapDN name, ModificationOperation mod, ServerEntry entry, OidRegistry oidRegistry )
460         throws NamingException
461     {
462         if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
463         {
464             return;
465         }
466 
467         Set<String> rdnAttributes = getRdnAttributes( name );
468         
469         for ( AttributeType attributeType:entry.getAttributeTypes() )
470         {
471             String id = attributeType.getName();
472 
473             if ( rdnAttributes.contains( id ) )
474             {
475                 EntryAttribute rdnAttr = entry.get( id );
476 
477                 // if the attribute values to delete are not specified then all values
478                 // for the attribute are to be deleted in which case we must just throw
479                 // a schema violation exception with the notAllowedOnRdn result code
480                 if ( rdnAttr.size() == 0 )
481                 {
482                     String msg = "Modify operation attempts to delete RDN attribute ";
483                     msg += id + " on entry " + name + " violates schema constraints";
484 
485                     if ( log.isInfoEnabled() )
486                     {
487                         log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
488                     }
489                     
490                     throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
491                 }
492 
493                 // from here on the modify operation replaces specific values
494                 // of the Rdn attribute so we must check to make sure all the old
495                 // rdn attribute values are present in the replacement set
496                 String rdnValue = getRdnValue( id, name, oidRegistry );
497 
498                 // if the old rdn value is not in the rdn attribute then
499                 // we must complain with a schema violation
500                 if ( !rdnAttr.contains( rdnValue ) )
501                 {
502                     String msg = "Modify operation attempts to delete RDN attribute values in use for ";
503                     msg += id + " on entry " + name + " and violates schema constraints";
504 
505                     if ( log.isInfoEnabled() )
506                     {
507                         log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
508                     }
509                     
510                     throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
511                 }
512             }
513         }
514     }
515 
516 
517     /**
518      * Makes sure a modify operation does not delete RDN attributes or their value.
519      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
520      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
521      * seen below:
522      * <p/>
523      * <pre>
524      *     The Modify Operation cannot be used to remove from an entry any of
525      *     its distinguished values, those values which form the entry's
526      *     relative distinguished name.  An attempt to do so will result in the
527      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
528      *     described in section 4.9 is used to rename an entry.
529      * </pre>
530      *
531      * @param name the distinguished name of the attribute being modified
532      * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
533      * @param attribute the attribute being modified
534      * @throws NamingException if the modify operation is removing an Rdn attribute
535      */
536     public static void preventRdnChangeOnModifyRemove( LdapDN name, ModificationOperation mod, ServerAttribute attribute, 
537         OidRegistry oidRegistry ) throws NamingException
538     {
539         if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
540         {
541             return;
542         }
543 
544         Set<String> rdnAttributes = getRdnAttributes( name );
545         String id = attribute.getId();
546 
547         if ( !rdnAttributes.contains( oidRegistry.getOid( id ) ) )
548         {
549             return;
550         }
551 
552         // if the attribute values to delete are not specified then all values
553         // for the attribute are to be deleted in which case we must just throw
554         // a schema violation exception with the notAllowedOnRdn result code
555         if ( attribute.size() == 0 )
556         {
557             String msg = "Modify operation attempts to delete RDN attribute ";
558             msg += id + " on entry " + name + " violates schema constraints";
559 
560             if ( log.isInfoEnabled() )
561             {
562                 log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
563             }
564             
565             throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
566         }
567 
568         // from here on the modify operation only deletes specific values
569         // of the Rdn attribute so we must check if one of those values
570         // are used by the Rdn attribute value pair for the name of the entry
571         String rdnValue = getRdnValue( id, name, oidRegistry );
572         
573         for ( Value<?> value:attribute )
574         {
575             if ( rdnValue.equals( (String)value.get() ) )
576             {
577                 String msg = "Modify operation attempts to delete RDN attribute values in use for ";
578                 msg += id + " on entry " + name + " and violates schema constraints";
579 
580                 if ( log.isInfoEnabled() )
581                 {
582                     log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
583                 }
584                 
585                 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
586             }
587         }
588     }
589 
590 
591     /**
592      * Makes sure a modify operation does not delete RDN attributes or their value.
593      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
594      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
595      * seen below:
596      * <p/>
597      * <pre>
598      *     The Modify Operation cannot be used to remove from an entry any of
599      *     its distinguished values, those values which form the entry's
600      *     relative distinguished name.  An attempt to do so will result in the
601      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
602      *     described in section 4.9 is used to rename an entry.
603      * </pre>
604      *
605      * @param name the distinguished name of the attribute being modified
606      * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
607      * @param entry
608      * @param oidRegistry
609      * @throws NamingException if the modify operation is removing an Rdn attribute
610      */
611     public static void preventRdnChangeOnModifyRemove( LdapDN name, ModificationOperation mod, ServerEntry entry, OidRegistry oidRegistry )
612         throws NamingException
613     {
614         if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
615         {
616             return;
617         }
618 
619         Set<String> rdnAttributes = getRdnAttributes( name );
620         
621         for ( AttributeType attributeType:entry.getAttributeTypes() )
622         {
623             String id = attributeType.getName();
624 
625             if ( rdnAttributes.contains( id ) )
626             {
627                 // if the attribute values to delete are not specified then all values
628                 // for the attribute are to be deleted in which case we must just throw
629                 // a schema violation exception with the notAllowedOnRdn result code
630                 if ( entry.get( id ).size() == 0 )
631                 {
632                     String msg = "Modify operation attempts to delete RDN attribute ";
633                     msg += id + " on entry " + name + " violates schema constraints";
634 
635                     if ( log.isInfoEnabled() )
636                     {
637                         log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
638                     }
639                     throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
640                 }
641 
642                 // from here on the modify operation only deletes specific values
643                 // of the Rdn attribute so we must check if one of those values
644                 // are used by the Rdn attribute value pair for the name of the entry
645                 String rdnValue = getRdnValue( id, name, oidRegistry );
646                 EntryAttribute rdnAttr = entry.get( id );
647                 
648                 for ( Value<?> value:rdnAttr )
649                 {
650                     if ( rdnValue.equals( (String)value.get() ) )
651                     {
652                         String msg = "Modify operation attempts to delete RDN attribute values in use for ";
653                         msg += id + " on entry " + name + " and violates schema constraints";
654 
655                         if ( log.isInfoEnabled() )
656                         {
657                             log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
658                         }
659                         throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOT_ALLOWED_ON_RDN );
660                     }
661                 }
662             }
663         }
664     }
665 
666 
667     /**
668      * Gets the Rdn attribute value. This method works even if the Rdn is
669      * composed of multiple attributes.
670      *
671      * @param id the attribute id of the Rdn attribute to return
672      * @param name the distinguished name of the entry
673      * @param oidRegistry the OID registry
674      * @return the Rdn attribute value corresponding to the id, or null if the
675      * attribute is not an rdn attribute
676      * @throws NamingException if the name is malformed in any way
677      */
678     private static String getRdnValue( String id, LdapDN name, OidRegistry oidRegistry ) throws NamingException
679     {
680         // Transform the rdnAttrId to it's OID counterPart
681         String idOid = oidRegistry.getOid( id );
682 
683         if ( idOid == null )
684         {
685             log.error( "The id {} does not have any OID. It should be a wrong AttributeType.", id);
686             throw new NamingException( "Wrong AttributeType, does not have an associated OID : " + id );
687         }
688 
689         String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
690 
691         for ( int ii = 0; ii < comps.length; ii++ )
692         {
693             String rdnAttrId = NamespaceTools.getRdnAttribute( comps[ii] );
694             
695             // Transform the rdnAttrId to it's OID counterPart
696             String rdnAttrOid = oidRegistry.getOid( rdnAttrId );
697 
698             if ( rdnAttrOid == null )
699             {
700                 log.error( "The id {} does not have any OID. It should be a wrong AttributeType.", rdnAttrOid);
701                 throw new NamingException( "Wrong AttributeType, does not have an associated OID : " + rdnAttrOid );
702             }
703 
704             if ( rdnAttrOid.equalsIgnoreCase( idOid ) )
705             {
706                 return NamespaceTools.getRdnValue( comps[ii] );
707             }
708         }
709 
710         return null;
711     }
712 
713 
714     /**
715      * Collects the set of Rdn attributes whether or not the Rdn is based on a
716      * single attribute or multiple attributes.
717      *
718      * @param name the distinguished name of an entry
719      * @return the set of attributes composing the Rdn for the name
720      * @throws NamingException if the syntax of the Rdn is incorrect
721      */
722     private static Set<String> getRdnAttributes( LdapDN name ) throws NamingException
723     {
724         String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
725         Set<String> attributes = new HashSet<String>();
726 
727         for ( int ii = 0; ii < comps.length; ii++ )
728         {
729             attributes.add( NamespaceTools.getRdnAttribute( comps[ii] ) );
730         }
731 
732         return attributes;
733     }
734 }