001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.schema;
021    
022    
023    import java.util.ArrayList;
024    import java.util.HashSet;
025    import java.util.List;
026    import java.util.Set;
027    
028    import org.apache.directory.server.i18n.I18n;
029    import org.apache.directory.shared.ldap.constants.SchemaConstants;
030    import org.apache.directory.shared.ldap.entry.EntryAttribute;
031    import org.apache.directory.shared.ldap.entry.ModificationOperation;
032    import org.apache.directory.shared.ldap.entry.ServerEntry;
033    import org.apache.directory.shared.ldap.entry.Value;
034    import org.apache.directory.shared.ldap.exception.LdapException;
035    import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
036    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
037    import org.apache.directory.shared.ldap.name.DN;
038    import org.apache.directory.shared.ldap.schema.AttributeType;
039    import org.apache.directory.shared.ldap.schema.ObjectClass;
040    import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
041    import org.apache.directory.shared.ldap.schema.SchemaManager;
042    import org.apache.directory.shared.ldap.schema.registries.ObjectClassRegistry;
043    import org.apache.directory.shared.ldap.util.NamespaceTools;
044    import org.slf4j.Logger;
045    import org.slf4j.LoggerFactory;
046    
047    
048    /**
049     * Performs schema checks on behalf of the SchemaInterceptor.
050     *
051     * TODO: we really need to refactor this code since there's much duplication
052     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053     * @version $Rev: 927839 $, $Date: 2010-03-26 14:25:10 +0100 (Fri, 26 Mar 2010) $
054     */
055    public class SchemaChecker
056    {
057        /** the SLF4J logger for this class */
058        private static Logger log = LoggerFactory.getLogger( SchemaChecker.class );
059    
060    
061        /**
062         * Makes sure modify operations do not leave the entry without a STRUCTURAL
063         * objectClass.  At least one STRUCTURAL objectClass must be specified for
064         * the entry after modifications take effect.
065         *
066         * @param registry the objectClass registry to lookup ObjectClass specifications
067         * @param name the name of the entry being modified
068         * @param mod the type of modification operation being performed (should be
069         * REMOVE_ATTRIBUTE)
070         * @param attribute the attribute being modified
071         * @throws LdapException if modify operations leave the entry inconsistent
072         * without a STRUCTURAL objectClass
073         */
074        public static void preventStructuralClassRemovalOnModifyReplace( SchemaManager schemaManager, DN name, ModificationOperation mod,
075            EntryAttribute attribute ) throws LdapException
076        {
077            if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
078            {
079                return;
080            }
081    
082            if ( !SchemaConstants.OBJECT_CLASS_AT.equalsIgnoreCase( attribute.getUpId() ) )
083            {
084                return;
085            }
086    
087            // whoever issued the modify operation is insane they want to delete
088            // all the objectClass values in which case we must throw an exception
089            if ( attribute.size() == 0 )
090            {
091                String msg = I18n.err( I18n.ERR_272, name );
092                
093                if ( log.isInfoEnabled() )
094                {
095                    log.info( msg + ".  Raising LdapSchemaViolationException." );
096                }
097                
098                throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
099            }
100    
101            // check that there is at least one structural objectClass in the replacement set
102            for ( Value<?> value:attribute )
103            {
104                ObjectClass ocType = schemaManager.getObjectClassRegistry().lookup( value.getString() );
105    
106                if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
107                {
108                    return;
109                }
110            }
111    
112            // no structural object classes exist for the entry in the replacement
113            // set for the objectClass attribute so we need to complain about that
114            String msg = I18n.err( I18n.ERR_272, name );
115            if ( log.isInfoEnabled() )
116            {
117                log.info( msg + ".  Raising LdapSchemaViolationException." );
118            }
119            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
120        }
121    
122    
123        /**
124         * Makes sure modify operations do not leave the entry without a STRUCTURAL
125         * objectClass.  At least one STRUCTURAL objectClass must be specified for
126         * the entry after modifications take effect.
127         *
128         * @param registry the objectClass registry to lookup ObjectClass specifications
129         * @param name the name of the entry being modified
130         * @param mod the type of modification operation being performed (should be
131         * REMOVE_ATTRIBUTE)
132         * @param entry the entry being modified
133         * @throws LdapException if modify operations leave the entry inconsistent
134         * without a STRUCTURAL objectClass
135         */
136        public static void preventStructuralClassRemovalOnModifyReplace( 
137            ObjectClassRegistry registry, DN name, ModificationOperation mod, ServerEntry entry ) throws LdapException
138        {
139            if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
140            {
141                return;
142            }
143    
144            EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
145            
146            if ( objectClass == null )
147            {
148                return;
149            }
150    
151            // whoever issued the modify operation is insane they want to delete
152            // all the objectClass values in which case we must throw an exception
153            if ( objectClass.size() == 0 )
154            {
155                String msg = I18n.err( I18n.ERR_272, name );
156                if ( log.isInfoEnabled() )
157                {
158                    log.info( msg + ".  Raising LdapSchemaViolationException." );
159                }
160                throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
161            }
162    
163            // check that there is at least one structural objectClass in the replacement set
164            for ( Value<?> value:objectClass )
165            {
166                ObjectClass ocType = registry.lookup( value.getString() );
167                
168                if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
169                {
170                    return;
171                }
172            }
173    
174            // no structural object classes exist for the entry in the replacement
175            // set for the objectClass attribute so we need to complain about that
176            String msg =  I18n.err( I18n.ERR_272, name );
177            if ( log.isInfoEnabled() )
178            {
179                log.info( msg + ".  Raising LdapSchemaViolationException." );
180            }
181            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
182        }
183    
184    
185        /**
186         * Makes sure modify operations do not leave the entry without a STRUCTURAL
187         * objectClass.  At least one STRUCTURAL objectClass must be specified for
188         * the entry after modifications take effect.
189         *
190         * @param registry the objectClass registry to lookup ObjectClass specifications
191         * @param name the name of the entry being modified
192         * @param mod the type of modification operation being performed (should be
193         * REMOVE_ATTRIBUTE)
194         * @param attribute the attribute being modified
195         * @param entryObjectClasses the entry being modified
196         * @throws LdapException if modify operations leave the entry inconsistent
197         * without a STRUCTURAL objectClass
198         */
199        public static void preventStructuralClassRemovalOnModifyRemove( SchemaManager schemaManager, DN name, ModificationOperation mod,
200            EntryAttribute attribute, EntryAttribute entryObjectClasses ) throws LdapException
201        {
202            if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
203            {
204                return;
205            }
206    
207            if ( !attribute.instanceOf( SchemaConstants.OBJECT_CLASS_AT ) )
208            {
209                return;
210            }
211            
212            // check if there is any attribute value as "".
213            // if there is remove it so that it will be considered as not even provided.
214            List<Value<?>> removed = new ArrayList<Value<?>>();
215            
216            // Fist gather the value to remove
217            for ( Value<?> value:attribute )
218            {
219                if ( value.getString().length() == 0 )
220                {
221                    removed.add( value );
222                }
223            }
224            
225            // Now remove the values from the attribute
226            for ( Value<?> value:removed )
227            {
228                attribute.remove( value );
229            }
230    
231            // whoever issued the modify operation is insane they want to delete
232            // all the objectClass values in which case we must throw an exception
233            if ( attribute.size() == 0 )
234            {
235                String msg =  I18n.err( I18n.ERR_272, name );
236                
237                if ( log.isInfoEnabled() )
238                {
239                    log.info( msg + ".  Raising LdapSchemaViolationException." );
240                }
241                
242                throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
243            }
244    
245            // remove all the objectClass attribute values from a cloned copy and then
246            // we can analyze what remains in this attribute to make sure a structural
247            // objectClass is present for the entry
248    
249            EntryAttribute cloned = entryObjectClasses.clone();
250            
251            for ( Value<?> value:attribute )
252            {
253                cloned.remove( value );
254            }
255    
256            // check resultant set of objectClass values for a structural objectClass
257            for ( Value<?> objectClass:cloned )
258            {
259                ObjectClass oc = schemaManager.getObjectClassRegistry().lookup( objectClass.getString() );
260                
261                if ( oc.getType() == ObjectClassTypeEnum.STRUCTURAL )
262                {
263                    return;
264                }
265            }
266    
267            // no structural object classes exist for the entry after the modifications
268            // to the objectClass attribute so we need to complain about that
269            String msg =  I18n.err( I18n.ERR_272, name );
270    
271            if ( log.isInfoEnabled() )
272            {
273                log.info( msg + ".  Raising LdapSchemaViolationException." );
274            }
275            
276            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED, msg );
277        }
278    
279    
280        /**
281         * Makes sure modify operations do not leave the entry without a STRUCTURAL
282         * objectClass.  At least one STRUCTURAL objectClass must be specified for
283         * the entry after modifications take effect.
284         *
285         * @param registry the objectClass registry to lookup ObjectClass specifications
286         * @param name the name of the entry being modified
287         * @param mod the type of modification operation being performed (should be
288         * REMOVE_ATTRIBUTE)
289         * @param attributes the attributes being modified
290         * @param entryObjectClasses the entry being modified
291         * @throws NamingException if modify operations leave the entry inconsistent
292         * without a STRUCTURAL objectClass
293         *
294        public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
295            Attributes attributes, Attribute entryObjectClasses ) throws NamingException
296        {
297            if ( mod != DirContext.REMOVE_ATTRIBUTE )
298            {
299                return;
300            }
301    
302            Attribute objectClass = attributes.get( SchemaConstants.OBJECT_CLASS_AT );
303            if ( objectClass == null )
304            {
305                return;
306            }
307            
308            // check if there is any attribute value as "".
309            // if there is remove it so that it will be considered as not even provided.
310            for( int ii = 0; ii < objectClass.size(); ii++ )
311            {
312                Object value = objectClass.get( ii );
313                if ( "".equals( value ) )
314                {
315                    objectClass.remove( ii );
316                }
317            }
318    
319            // whoever issued the modify operation is insane they want to delete
320            // all the objectClass values in which case we must throw an exception
321            if ( objectClass.size() == 0 )
322            {
323                String msg = "Modify operation leaves no structural objectClass for entry " + name;
324                if ( log.isInfoEnabled() )
325                {
326                    log.info( msg + ".  Raising LdapSchemaViolationException." );
327                }
328                throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
329            }
330    
331            // remove all the objectClass attribute values from a cloned copy and then
332            // we can analyze what remains in this attribute to make sure a structural
333            // objectClass is present for the entry
334    
335            Attribute cloned = ( Attribute ) entryObjectClasses.clone();
336            for ( int ii = 0; ii < objectClass.size(); ii++ )
337            {
338                cloned.remove( objectClass.get( ii ) );
339            }
340    
341            // check resultant set of objectClass values for a structural objectClass
342            for ( int ii = 0; ii < cloned.size(); ii++ )
343            {
344                ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
345                if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
346                {
347                    return;
348                }
349            }
350    
351            // no structural object classes exist for the entry after the modifications
352            // to the objectClass attribute so we need to complain about that
353            String msg = "Modify operation leaves no structural objectClass for entry " + name;
354            if ( log.isInfoEnabled() )
355            {
356                log.info( msg + ".  Raising LdapSchemaViolationException." );
357            }
358            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_MODS_PROHIBITED );
359        }
360        */
361    
362        /**
363         * Makes sure a modify operation does not replace RDN attributes or their value.
364         * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
365         * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
366         * seen below:
367         * <p/>
368         * <pre>
369         *     The Modify Operation cannot be used to remove from an entry any of
370         *     its distinguished values, those values which form the entry's
371         *     relative distinguished name.  An attempt to do so will result in the
372         *     server returning the error notAllowedOnRDN.  The Modify DN Operation
373         *     described in section 4.9 is used to rename an entry.
374         * </pre>
375         *
376         * @param name the distinguished name of the attribute being modified
377         * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
378         * @param attribute the attribute being modified
379         * @param oidRegistry
380         * @throws LdapException if the modify operation is removing an Rdn attribute
381         */
382        public static void preventRdnChangeOnModifyReplace( DN name, ModificationOperation mod, 
383            EntryAttribute attribute, SchemaManager schemaManager )
384            throws LdapException
385        {
386            if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
387            {
388                return;
389            }
390    
391            Set<String> rdnAttributes = getRdnAttributes( name );
392            String id = schemaManager.getAttributeTypeRegistry().getOidByName( attribute.getUpId() );
393    
394            if ( !rdnAttributes.contains( id ) )
395            {
396                return;
397            }
398    
399            // if the attribute values to delete are not specified then all values
400            // for the attribute are to be deleted in which case we must just throw
401            // a schema violation exception with the notAllowedOnRdn result code
402            if ( attribute.size() == 0 )
403            {
404                String msg = I18n.err( I18n.ERR_273, id, name );
405    
406                if ( log.isInfoEnabled() )
407                {
408                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
409                }
410                throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
411            }
412    
413            // from here on the modify operation replaces specific values
414            // of the Rdn attribute so we must check to make sure all the old
415            // rdn attribute values are present in the replacement set
416            String rdnValue = getRdnValue( id, name, schemaManager );
417            
418            for ( int ii = 0; ii < attribute.size(); ii++ )
419            {
420                // if the old rdn value is not in the rdn attribute then
421                // we must complain with a schema violation
422                if ( !attribute.contains( rdnValue ) )
423                {
424                    String msg = I18n.err( I18n.ERR_274, id, name );
425    
426                    if ( log.isInfoEnabled() )
427                    {
428                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
429                    }
430                    throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
431                }
432            }
433        }
434    
435    
436        /**
437         * Makes sure a modify operation does not replace RDN attributes or their value.
438         * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
439         * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
440         * seen below:
441         * <p/>
442         * <pre>
443         *     The Modify Operation cannot be used to remove from an entry any of
444         *     its distinguished values, those values which form the entry's
445         *     relative distinguished name.  An attempt to do so will result in the
446         *     server returning the error notAllowedOnRDN.  The Modify DN Operation
447         *     described in section 4.9 is used to rename an entry.
448         * </pre>
449         *
450         * @param name the distinguished name of the attribute being modified
451         * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
452         * @param entry
453         * @param oidRegistry
454         * @throws LdapException if the modify operation is removing an Rdn attribute
455         */
456        public static void preventRdnChangeOnModifyReplace( 
457            DN name, ModificationOperation mod, ServerEntry entry, 
458            SchemaManager schemaManager )
459            throws LdapException
460        {
461            if ( mod != ModificationOperation.REPLACE_ATTRIBUTE )
462            {
463                return;
464            }
465    
466            Set<String> rdnAttributes = getRdnAttributes( name );
467            
468            for ( AttributeType attributeType:entry.getAttributeTypes() )
469            {
470                String id = attributeType.getName();
471    
472                if ( rdnAttributes.contains( id ) )
473                {
474                    EntryAttribute rdnAttr = entry.get( id );
475    
476                    // if the attribute values to delete are not specified then all values
477                    // for the attribute are to be deleted in which case we must just throw
478                    // a schema violation exception with the notAllowedOnRdn result code
479                    if ( rdnAttr.size() == 0 )
480                    {
481                        String msg = I18n.err( I18n.ERR_273, id, name );
482    
483                        if ( log.isInfoEnabled() )
484                        {
485                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
486                        }
487                        
488                        throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
489                    }
490    
491                    // from here on the modify operation replaces specific values
492                    // of the Rdn attribute so we must check to make sure all the old
493                    // rdn attribute values are present in the replacement set
494                    String rdnValue = getRdnValue( id, name, schemaManager );
495    
496                    // if the old rdn value is not in the rdn attribute then
497                    // we must complain with a schema violation
498                    if ( !rdnAttr.contains( rdnValue ) )
499                    {
500                        String msg = I18n.err( I18n.ERR_274, id, name );
501    
502                        if ( log.isInfoEnabled() )
503                        {
504                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
505                        }
506                        
507                        throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
508                    }
509                }
510            }
511        }
512    
513    
514        /**
515         * Makes sure a modify operation does not delete RDN attributes or their value.
516         * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
517         * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
518         * seen below:
519         * <p/>
520         * <pre>
521         *     The Modify Operation cannot be used to remove from an entry any of
522         *     its distinguished values, those values which form the entry's
523         *     relative distinguished name.  An attempt to do so will result in the
524         *     server returning the error notAllowedOnRDN.  The Modify DN Operation
525         *     described in section 4.9 is used to rename an entry.
526         * </pre>
527         *
528         * @param name the distinguished name of the attribute being modified
529         * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
530         * @param attribute the attribute being modified
531         * @throws LdapException if the modify operation is removing an Rdn attribute
532         */
533        public static void preventRdnChangeOnModifyRemove( DN name, ModificationOperation mod, EntryAttribute attribute, 
534            SchemaManager schemaManager ) throws LdapException
535        {
536            if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
537            {
538                return;
539            }
540    
541            Set<String> rdnAttributes = getRdnAttributes( name );
542            String id = attribute.getId();
543    
544            if ( !rdnAttributes.contains( schemaManager.getAttributeTypeRegistry().getOidByName( id ) ) )
545            {
546                return;
547            }
548    
549            // if the attribute values to delete are not specified then all values
550            // for the attribute are to be deleted in which case we must just throw
551            // a schema violation exception with the notAllowedOnRdn result code
552            if ( attribute.size() == 0 )
553            {
554                String msg = I18n.err( I18n.ERR_273, id, name );
555    
556                if ( log.isInfoEnabled() )
557                {
558                    log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
559                }
560                
561                throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
562            }
563    
564            // from here on the modify operation only deletes specific values
565            // of the Rdn attribute so we must check if one of those values
566            // are used by the Rdn attribute value pair for the name of the entry
567            String rdnValue = getRdnValue( id, name, schemaManager );
568            
569            for ( Value<?> value:attribute )
570            {
571                if ( rdnValue.equals( value.getString() ) )
572                {
573                    String msg = I18n.err( I18n.ERR_274, id, name );
574    
575                    if ( log.isInfoEnabled() )
576                    {
577                        log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
578                    }
579                    
580                    throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
581                }
582            }
583        }
584    
585    
586        /**
587         * Makes sure a modify operation does not delete RDN attributes or their value.
588         * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
589         * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
590         * seen below:
591         * <p/>
592         * <pre>
593         *     The Modify Operation cannot be used to remove from an entry any of
594         *     its distinguished values, those values which form the entry's
595         *     relative distinguished name.  An attempt to do so will result in the
596         *     server returning the error notAllowedOnRDN.  The Modify DN Operation
597         *     described in section 4.9 is used to rename an entry.
598         * </pre>
599         *
600         * @param name the distinguished name of the attribute being modified
601         * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
602         * @param entry
603         * @param oidRegistry
604         * @throws LdapException if the modify operation is removing an Rdn attribute
605         */
606        public static void preventRdnChangeOnModifyRemove( DN name, ModificationOperation mod, 
607            ServerEntry entry, SchemaManager schemaManager )
608            throws LdapException
609        {
610            if ( mod != ModificationOperation.REMOVE_ATTRIBUTE )
611            {
612                return;
613            }
614    
615            Set<String> rdnAttributes = getRdnAttributes( name );
616            
617            for ( AttributeType attributeType:entry.getAttributeTypes() )
618            {
619                String id = attributeType.getName();
620    
621                if ( rdnAttributes.contains( id ) )
622                {
623                    // if the attribute values to delete are not specified then all values
624                    // for the attribute are to be deleted in which case we must just throw
625                    // a schema violation exception with the notAllowedOnRdn result code
626                    if ( entry.get( id ).size() == 0 )
627                    {
628                        String msg = I18n.err( I18n.ERR_273, id, name );
629    
630                        if ( log.isInfoEnabled() )
631                        {
632                            log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
633                        }
634                        throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
635                    }
636    
637                    // from here on the modify operation only deletes specific values
638                    // of the Rdn attribute so we must check if one of those values
639                    // are used by the Rdn attribute value pair for the name of the entry
640                    String rdnValue = getRdnValue( id, name, schemaManager );
641                    EntryAttribute rdnAttr = entry.get( id );
642                    
643                    for ( Value<?> value:rdnAttr )
644                    {
645                        if ( rdnValue.equals( value.getString() ) )
646                        {
647                            String msg = I18n.err( I18n.ERR_274, id, name );
648    
649                            if ( log.isInfoEnabled() )
650                            {
651                                log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
652                            }
653                            throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
654                        }
655                    }
656                }
657            }
658        }
659    
660    
661        /**
662         * Gets the Rdn attribute value. This method works even if the Rdn is
663         * composed of multiple attributes.
664         *
665         * @param id the attribute id of the Rdn attribute to return
666         * @param name the distinguished name of the entry
667         * @param oidRegistry the OID registry
668         * @return the Rdn attribute value corresponding to the id, or null if the
669         * attribute is not an rdn attribute
670         * @throws LdapException if the name is malformed in any way
671         */
672        private static String getRdnValue( String id, DN name, SchemaManager schemaManager ) throws LdapException
673        {
674            // Transform the rdnAttrId to it's OID counterPart
675            String idOid = schemaManager.getAttributeTypeRegistry().getOidByName( id );
676    
677            if ( idOid == null )
678            {
679                log.error( I18n.err( I18n.ERR_43, id ) );
680                throw new LdapException( I18n.err( I18n.ERR_44, id ) );
681            }
682    
683            String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
684    
685            for ( int ii = 0; ii < comps.length; ii++ )
686            {
687                String rdnAttrId = NamespaceTools.getRdnAttribute( comps[ii] );
688                
689                // Transform the rdnAttrId to it's OID counterPart
690                String rdnAttrOid = schemaManager.getAttributeTypeRegistry().getOidByName( rdnAttrId );
691    
692                if ( rdnAttrOid == null )
693                {
694                    log.error( I18n.err( I18n.ERR_43, rdnAttrOid ) );
695                    throw new LdapException( I18n.err( I18n.ERR_44, rdnAttrOid ) );
696                }
697    
698                if ( rdnAttrOid.equalsIgnoreCase( idOid ) )
699                {
700                    return NamespaceTools.getRdnValue( comps[ii] );
701                }
702            }
703    
704            return null;
705        }
706    
707    
708        /**
709         * Collects the set of Rdn attributes whether or not the Rdn is based on a
710         * single attribute or multiple attributes.
711         *
712         * @param name the distinguished name of an entry
713         * @return the set of attributes composing the Rdn for the name
714         * @throws LdapException if the syntax of the Rdn is incorrect
715         */
716        private static Set<String> getRdnAttributes( DN name ) throws LdapException
717        {
718            String[] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
719            Set<String> attributes = new HashSet<String>();
720    
721            for ( int ii = 0; ii < comps.length; ii++ )
722            {
723                attributes.add( NamespaceTools.getRdnAttribute( comps[ii] ) );
724            }
725    
726            return attributes;
727        }
728    }