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  package org.apache.directory.server.core.api.entry;
20  
21  
22  import java.util.ArrayList;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.NoSuchElementException;
26  import java.util.Set;
27  
28  import javax.naming.NamingEnumeration;
29  import javax.naming.NamingException;
30  import javax.naming.directory.Attributes;
31  import javax.naming.directory.BasicAttribute;
32  import javax.naming.directory.BasicAttributes;
33  import javax.naming.directory.DirContext;
34  import javax.naming.directory.ModificationItem;
35  import javax.naming.directory.SearchResult;
36  
37  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
38  import org.apache.directory.api.ldap.model.entry.Attribute;
39  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
40  import org.apache.directory.api.ldap.model.entry.DefaultEntry;
41  import org.apache.directory.api.ldap.model.entry.DefaultModification;
42  import org.apache.directory.api.ldap.model.entry.Entry;
43  import org.apache.directory.api.ldap.model.entry.Modification;
44  import org.apache.directory.api.ldap.model.entry.ModificationOperation;
45  import org.apache.directory.api.ldap.model.entry.Value;
46  import org.apache.directory.api.ldap.model.exception.LdapException;
47  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeTypeException;
48  import org.apache.directory.api.ldap.model.name.Dn;
49  import org.apache.directory.api.ldap.model.schema.AttributeType;
50  import org.apache.directory.api.ldap.model.schema.AttributeTypeOptions;
51  import org.apache.directory.api.ldap.model.schema.SchemaManager;
52  import org.apache.directory.api.ldap.model.schema.SchemaUtils;
53  import org.apache.directory.api.util.EmptyEnumeration;
54  import org.apache.directory.api.util.Strings;
55  import org.apache.directory.server.core.api.interceptor.context.FilteringOperationContext;
56  import org.apache.directory.server.i18n.I18n;
57  
58  
59  /**
60   * A helper class used to manipulate Entries, Attributes and Values.
61   *
62   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
63   */
64  public final class ServerEntryUtils
65  {
66      private ServerEntryUtils()
67      {
68      }
69  
70  
71      /**
72       * Convert a ServerAttribute into a BasicAttribute. The Dn is lost
73       * during this conversion, as the Attributes object does not store
74       * this element.
75       *
76       * @param entryAttribute The Server entry to convert
77       * @return An instance of a AttributesImpl() object
78       */
79      public static javax.naming.directory.Attribute toBasicAttribute( Attribute entryAttribute )
80      {
81          AttributeType attributeType = entryAttribute.getAttributeType();
82  
83          javax.naming.directory.Attribute attribute = new BasicAttribute( attributeType.getName() );
84  
85          for ( Value value : entryAttribute )
86          {
87              if ( attributeType.isHR() )
88              {
89                  attribute.add( value.getString() );
90              }
91              else
92              {
93                  attribute.add( value.getBytes() );
94              }
95          }
96  
97          return attribute;
98      }
99  
100 
101     /**
102      * Convert a ServerEntry into a BasicAttributes. The Dn is lost
103      * during this conversion, as the Attributes object does not store
104      * this element.
105      *
106      * @param entry The entry to convert
107      * @return An instance of a AttributesImpl() object
108      */
109     public static Attributes toBasicAttributes( Entry entry )
110     {
111         if ( entry == null )
112         {
113             return null;
114         }
115 
116         Attributes attributes = new BasicAttributes( true );
117 
118         for ( Attribute attribute : entry.getAttributes() )
119         {
120             AttributeType attributeType = attribute.getAttributeType();
121             Attribute attr = entry.get( attributeType );
122 
123             // Deal with a special case : an entry without any ObjectClass
124             if ( attributeType.getOid().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) && attr.size() == 0 )
125             {
126                 // We don't have any objectClass, just dismiss this element
127                 continue;
128             }
129 
130             attributes.put( toBasicAttribute( attr ) );
131         }
132 
133         return attributes;
134     }
135 
136 
137     /**
138      * Convert a BasicAttribute or a AttributeImpl to a ServerAtribute
139      *
140      * @param attribute the BasicAttributes or AttributesImpl instance to convert
141      * @param attributeType The AttributeType to use
142      * @return An instance of a ServerEntry object
143      * 
144      * @throws LdapException If we had an incorrect attribute
145      */
146     public static Attribute toServerAttribute( javax.naming.directory.Attribute attribute, AttributeType attributeType )
147         throws LdapException
148     {
149         if ( attribute == null )
150         {
151             return null;
152         }
153 
154         try
155         {
156             Attribute serverAttribute = new DefaultAttribute( attributeType );
157 
158             for ( NamingEnumeration<?> values = attribute.getAll(); values.hasMoreElements(); )
159             {
160                 Object value = values.nextElement();
161                 int nbAdded = 0;
162 
163                 if ( value == null )
164                 {
165                     continue;
166                 }
167 
168                 if ( serverAttribute.isHumanReadable() )
169                 {
170                     if ( value instanceof String )
171                     {
172                         nbAdded = serverAttribute.add( ( String ) value );
173                     }
174                     else if ( value instanceof byte[] )
175                     {
176                         nbAdded = serverAttribute.add( Strings.utf8ToString( ( byte[] ) value ) );
177                     }
178                     else
179                     {
180                         throw new LdapInvalidAttributeTypeException();
181                     }
182                 }
183                 else
184                 {
185                     if ( value instanceof String )
186                     {
187                         nbAdded = serverAttribute.add( Strings.getBytesUtf8( ( String ) value ) );
188                     }
189                     else if ( value instanceof byte[] )
190                     {
191                         nbAdded = serverAttribute.add( ( byte[] ) value );
192                     }
193                     else
194                     {
195                         throw new LdapInvalidAttributeTypeException();
196                     }
197                 }
198 
199                 if ( nbAdded == 0 )
200                 {
201                     throw new LdapInvalidAttributeTypeException();
202                 }
203             }
204 
205             return serverAttribute;
206         }
207         catch ( NamingException ne )
208         {
209             throw new LdapInvalidAttributeTypeException();
210         }
211     }
212 
213 
214     /**
215      * Convert a BasicAttributes or a AttributesImpl to a ServerEntry
216      *
217      * @param attributes the BasicAttributes or AttributesImpl instance to convert
218      * @param dn The Dn which is needed by the ServerEntry
219      * @param schemaManager The SchemaManager instance
220      * @return An instance of a ServerEntry object
221      * 
222      * @throws LdapInvalidAttributeTypeException If we get an invalid attribute
223      */
224     public static Entry toServerEntry( Attributes attributes, Dn dn, SchemaManager schemaManager )
225         throws LdapInvalidAttributeTypeException
226     {
227         if ( attributes instanceof BasicAttributes )
228         {
229             try
230             {
231                 Entry entry = new DefaultEntry( schemaManager, dn );
232 
233                 for ( NamingEnumeration<? extends javax.naming.directory.Attribute> attrs = attributes.getAll(); attrs
234                     .hasMoreElements(); )
235                 {
236                     javax.naming.directory.Attribute attr = attrs.nextElement();
237 
238                     String attributeId = attr.getID();
239                     String id = SchemaUtils.stripOptions( attributeId );
240                     Set<String> options = SchemaUtils.getOptions( attributeId );
241                     // TODO : handle options.
242                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
243                     Attribute serverAttribute = ServerEntryUtils.toServerAttribute( attr, attributeType );
244 
245                     if ( serverAttribute != null )
246                     {
247                         entry.put( serverAttribute );
248                     }
249                 }
250 
251                 return entry;
252             }
253             catch ( LdapException ne )
254             {
255                 throw new LdapInvalidAttributeTypeException( ne.getLocalizedMessage() );
256             }
257         }
258         else
259         {
260             return null;
261         }
262     }
263 
264 
265     /**
266      * Gets the target entry as it would look after a modification operation 
267      * was performed on it.
268      * 
269      * @param mod the modification
270      * @param entry the source entry that is modified
271      * @param schemaManager The SchemaManager instance
272      * @return the resultant entry after the modification has taken place
273      * @throws LdapException if there are problems accessing attributes
274      */
275     public static Entry getTargetEntry( Modification mod, Entry entry, SchemaManager schemaManager )
276         throws LdapException
277     {
278         Entry targetEntry = entry.clone();
279         ModificationOperation modOp = mod.getOperation();
280         String id = mod.getAttribute().getUpId();
281         AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
282 
283         switch ( modOp )
284         {
285             case REPLACE_ATTRIBUTE:
286                 targetEntry.put( mod.getAttribute() );
287                 break;
288 
289             case REMOVE_ATTRIBUTE:
290                 Attribute toBeRemoved = mod.getAttribute();
291 
292                 if ( toBeRemoved.size() == 0 )
293                 {
294                     targetEntry.removeAttributes( id );
295                 }
296                 else
297                 {
298                     Attribute existing = targetEntry.get( id );
299 
300                     if ( existing != null )
301                     {
302                         for ( Value value : toBeRemoved )
303                         {
304                             existing.remove( value );
305                         }
306                     }
307                 }
308                 break;
309 
310             case ADD_ATTRIBUTE:
311                 Attribute combined = new DefaultAttribute( id, attributeType );
312                 Attribute toBeAdded = mod.getAttribute();
313                 Attribute existing = entry.get( id );
314 
315                 if ( existing != null )
316                 {
317                     for ( Value value : existing )
318                     {
319                         combined.add( value );
320                     }
321                 }
322 
323                 for ( Value value : toBeAdded )
324                 {
325                     combined.add( value );
326                 }
327 
328                 targetEntry.put( combined );
329                 break;
330 
331             default:
332                 throw new IllegalStateException( I18n.err( I18n.ERR_464, modOp ) );
333         }
334 
335         return targetEntry;
336     }
337 
338 
339     /**
340      * Creates a new attribute which contains the values representing the union
341      * of two attributes. If one attribute is null then the resultant attribute
342      * returned is a copy of the non-null attribute. If both are null then we
343      * cannot determine the attribute ID and an {@link IllegalArgumentException}
344      * is raised.
345      * 
346      * @param attr0 the first attribute
347      * @param attr1 the second attribute
348      * @return a new attribute with the union of values from both attribute
349      *         arguments
350      * @throws LdapException if there are problems accessing attribute values
351      */
352     public static Attribute getUnion( Attribute attr0, Attribute attr1 ) throws LdapException
353     {
354         if ( attr0 == null && attr1 == null )
355         {
356             throw new IllegalArgumentException( I18n.err( I18n.ERR_465 ) );
357         }
358         else if ( attr0 == null )
359         {
360             return attr1.clone();
361         }
362         else if ( attr1 == null )
363         {
364             return attr0.clone();
365         }
366         else if ( !attr0.getAttributeType().equals( attr1.getAttributeType() ) )
367         {
368             throw new IllegalArgumentException( I18n.err( I18n.ERR_466 ) );
369         }
370 
371         Attribute attr = attr0.clone();
372 
373         for ( Value value : attr1 )
374         {
375             attr.add( value );
376         }
377 
378         return attr;
379     }
380 
381 
382     /**
383      * Convert a ModificationItem to an instance of a ServerModification object
384      *
385      * @param modificationImpl the modification instance to convert
386      * @param attributeType the associated attributeType
387      * @return a instance of a ServerModification object
388      */
389     private static Modification toServerModification( ModificationItem modificationImpl, AttributeType attributeType )
390         throws LdapException
391     {
392         ModificationOperation operation;
393 
394         switch ( modificationImpl.getModificationOp() )
395         {
396             case DirContext.REMOVE_ATTRIBUTE:
397                 operation = ModificationOperation.REMOVE_ATTRIBUTE;
398                 break;
399 
400             case DirContext.REPLACE_ATTRIBUTE:
401                 operation = ModificationOperation.REPLACE_ATTRIBUTE;
402                 break;
403 
404             case DirContext.ADD_ATTRIBUTE:
405             default:
406                 operation = ModificationOperation.ADD_ATTRIBUTE;
407                 break;
408 
409         }
410 
411         return new DefaultModification( operation,
412             ServerEntryUtils.toServerAttribute( modificationImpl.getAttribute(), attributeType ) );
413     }
414 
415 
416     /**
417      * 
418      * Convert a list of ModificationItemImpl to a list of LDAP API Modifications
419      *
420      * @param modificationItems The modificationItems to convert
421      * @param schemaManager The SchemaManager instance
422      * @return A list of converted Modification
423      * @throws LdapException If the conversion failed
424      */
425     public static List<Modification> convertToServerModification( List<ModificationItem> modificationItems,
426         SchemaManager schemaManager ) throws LdapException
427     {
428         if ( modificationItems != null )
429         {
430             List<Modification> modifications = new ArrayList<>( modificationItems.size() );
431 
432             for ( ModificationItem modificationItem : modificationItems )
433             {
434                 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( modificationItem
435                     .getAttribute().getID() );
436                 modifications.add( toServerModification( modificationItem, attributeType ) );
437             }
438 
439             return modifications;
440         }
441         else
442         {
443             return null;
444         }
445     }
446 
447 
448     /**
449      * Convert a Modification to an instance of a ServerModification object.
450      *
451      * @param modificationImpl the modification instance to convert
452      * @param attributeType the associated attributeType
453      * @return a instance of a ServerModification object
454      */
455     private static Modification toServerModification( Modification modification, AttributeType attributeType )
456         throws LdapException
457     {
458         return new DefaultModification(
459             modification.getOperation(),
460             new DefaultAttribute( attributeType, modification.getAttribute() ) );
461     }
462 
463 
464     /**
465      * Convert a JNDI set of Modifications to LDAP API Modifications
466      * 
467      * @param modifications The modifications to convert
468      * @param schemaManager The SchemaManager instance
469      * @return The list of converted Modifications
470      * @throws LdapException If the conversion failed
471      */
472     public static List<Modification> toServerModification( Modification[] modifications,
473         SchemaManager schemaManager ) throws LdapException
474     {
475         if ( modifications != null )
476         {
477             List<Modification> modificationsList = new ArrayList<>();
478 
479             for ( Modification modification : modifications )
480             {
481                 String attributeId = modification.getAttribute().getUpId();
482                 String id = stripOptions( attributeId );
483                 modification.getAttribute().setUpId( id );
484                 Set<String> options = getOptions( attributeId );
485 
486                 // -------------------------------------------------------------------
487                 // DIRSERVER-646 Fix: Replacing an unknown attribute with no values 
488                 // (deletion) causes an error
489                 // -------------------------------------------------------------------
490                 if ( !schemaManager.getAttributeTypeRegistry().contains( id )
491                     && modification.getAttribute().size() == 0
492                     && modification.getOperation() == ModificationOperation.REPLACE_ATTRIBUTE )
493                 {
494                     // The attributeType does not exist in the schema.
495                     // It's an error
496                     String message = I18n.err( I18n.ERR_467, id );
497                     throw new LdapInvalidAttributeTypeException( message );
498                 }
499                 else
500                 {
501                     // TODO : handle options
502                     AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
503                     modificationsList.add( toServerModification( modification, attributeType ) );
504                 }
505             }
506 
507             return modificationsList;
508         }
509         else
510         {
511             return null;
512         }
513     }
514 
515 
516     /**
517      * Convert a JNDI set of ModificationItems to LDAP API Modifications
518      * 
519      * @param modifications The modificationItems to convert
520      * @param schemaManager The SchemaManager instance
521      * @return The list of converted ModificationItems
522      * @throws LdapException If the conversion failed
523      */
524     public static List<Modification> toServerModification( ModificationItem[] modifications,
525         SchemaManager schemaManager ) throws LdapException
526     {
527         if ( modifications != null )
528         {
529             List<Modification> modificationsList = new ArrayList<>();
530 
531             for ( ModificationItem modification : modifications )
532             {
533                 String attributeId = modification.getAttribute().getID();
534                 String id = stripOptions( attributeId );
535                 Set<String> options = getOptions( attributeId );
536 
537                 // -------------------------------------------------------------------
538                 // DIRSERVER-646 Fix: Replacing an unknown attribute with no values 
539                 // (deletion) causes an error
540                 // -------------------------------------------------------------------
541 
542                 // TODO - after removing JNDI we need to make the server handle 
543                 // this in the codec
544 
545                 if ( !schemaManager.getAttributeTypeRegistry().contains( id )
546                     && modification.getAttribute().size() == 0
547                     && modification.getModificationOp() == DirContext.REPLACE_ATTRIBUTE )
548                 {
549                     continue;
550                 }
551 
552                 // -------------------------------------------------------------------
553                 // END DIRSERVER-646 Fix
554                 // -------------------------------------------------------------------
555 
556                 // TODO : handle options
557                 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( id );
558                 modificationsList.add( toServerModification( modification, attributeType ) );
559             }
560 
561             return modificationsList;
562         }
563         else
564         {
565             return null;
566         }
567     }
568 
569 
570     /**
571      * Utility method to extract a modification item from an array of modifications.
572      * 
573      * @param mods the array of ModificationItems to extract the Attribute from.
574      * @param type the attributeType spec of the Attribute to extract
575      * @return the modification item on the attributeType specified
576      */
577     public static Modification getModificationItem( List<Modification> mods, AttributeType type )
578     {
579         for ( Modification modification : mods )
580         {
581             Attribute attribute = modification.getAttribute();
582 
583             if ( attribute.getAttributeType() == type )
584             {
585                 return modification;
586             }
587         }
588 
589         return null;
590     }
591 
592 
593     /**
594      * Utility method to extract an attribute from a list of modifications.
595      * 
596      * @param mods the list of ModificationItems to extract the Attribute from.
597      * @param type the attributeType spec of the Attribute to extract
598      * @return the extract Attribute or null if no such attribute exists
599      */
600     public static Attribute getAttribute( List<Modification> mods, AttributeType type )
601     {
602         Modification mod = getModificationItem( mods, type );
603 
604         if ( mod != null )
605         {
606             return mod.getAttribute();
607         }
608 
609         return null;
610     }
611 
612 
613     /**
614      * Encapsulate a ServerSearchResult enumeration into a SearchResult enumeration
615      * @param result The ServerSearchResult enumeration
616      * @return A SearchResultEnumeration
617      */
618     public static NamingEnumeration<SearchResult> toSearchResultEnum( final NamingEnumeration<ServerSearchResult> result )
619     {
620         if ( result instanceof EmptyEnumeration<?> )
621         {
622             return new EmptyEnumeration<>();
623         }
624 
625         return new NamingEnumeration<SearchResult>()
626         {
627             public void close() throws NamingException
628             {
629                 result.close();
630             }
631 
632 
633             /**
634              * @see javax.naming.NamingEnumeration#hasMore()
635              */
636             public boolean hasMore() throws NamingException
637             {
638                 return result.hasMore();
639             }
640 
641 
642             /**
643              * @see javax.naming.NamingEnumeration#next()
644              */
645             public SearchResult next() throws NamingException
646             {
647                 ServerSearchResult rec = result.next();
648 
649                 return new SearchResult(
650                     rec.getDn().getName(),
651                     rec.getObject(),
652                     toBasicAttributes( rec.getServerEntry() ),
653                     rec.isRelative() );
654             }
655 
656 
657             /**
658              * @see java.util.Enumeration#hasMoreElements()
659              */
660             public boolean hasMoreElements()
661             {
662                 return result.hasMoreElements();
663             }
664 
665 
666             /**
667              * @see java.util.Enumeration#nextElement()
668              */
669             public SearchResult nextElement()
670             {
671                 try
672                 {
673                     ServerSearchResult rec = result.next();
674 
675                     return new SearchResult(
676                         rec.getDn().getName(),
677                         rec.getObject(),
678                         toBasicAttributes( rec.getServerEntry() ),
679                         rec.isRelative() );
680                 }
681                 catch ( NamingException ne )
682                 {
683                     NoSuchElementException nsee =
684                         new NoSuchElementException( I18n.err( I18n.ERR_468 ) );
685                     nsee.initCause( ne );
686                     throw nsee;
687                 }
688             }
689         };
690     }
691 
692 
693     /**
694      * Remove the options from the attributeType, and returns the ID.
695      * 
696      * RFC 4512 :
697      * attributedescription = attributetype options
698      * attributetype = oid
699      * options = *( SEMI option )
700      * option = 1*keychar
701      */
702     private static String stripOptions( String attributeId )
703     {
704         int optionsPos = attributeId.indexOf( ';' );
705 
706         if ( optionsPos != -1 )
707         {
708             return attributeId.substring( 0, optionsPos );
709         }
710         else
711         {
712             return attributeId;
713         }
714     }
715 
716 
717     /**
718      * Get the options from the attributeType.
719      * 
720      * For instance, given :
721      * jpegphoto;binary;lang=jp
722      * 
723      * your get back a set containing { "binary", "lang=jp" }
724      */
725     private static Set<String> getOptions( String attributeId )
726     {
727         int optionsPos = attributeId.indexOf( ';' );
728 
729         if ( optionsPos != -1 )
730         {
731             Set<String> options = new HashSet<>();
732 
733             String[] res = attributeId.substring( optionsPos + 1 ).split( ";" );
734 
735             for ( String option : res )
736             {
737                 if ( !Strings.isEmpty( option ) )
738                 {
739                     options.add( option );
740                 }
741             }
742 
743             return options;
744         }
745         else
746         {
747             return null;
748         }
749     }
750 
751 
752     /**
753      * Filters an entry accordingly to the requested Attribute list.
754      * 
755      * @param schemaManager The SchemaManager instance
756      * @param operationContext The SearchingOperationContext
757      * @param entry The entry to filter
758      * @throws LdapException If the filtering fails
759      */
760     public static void filterContents( SchemaManager schemaManager, FilteringOperationContext operationContext,
761         Entry entry ) throws LdapException
762     {
763         boolean typesOnly = operationContext.isTypesOnly();
764 
765         boolean returnAll = ( operationContext.isAllOperationalAttributes() && operationContext.isAllUserAttributes() )
766             && ( !typesOnly );
767 
768         if ( returnAll )
769         {
770             return;
771         }
772 
773         // for special handling of entryDN attribute, see DIRSERVER-1902
774         Entry originalEntry = ( ( ClonedServerEntry ) entry ).getOriginalEntry();
775 
776         AttributeType entryDnType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ENTRY_DN_AT_OID );
777         AttributeType refType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.REF_AT_OID );
778 
779         // First, remove all the attributes if we have the NoAttribute flag set to true
780         if ( operationContext.isNoAttributes() )
781         {
782             for ( Attribute attribute : originalEntry )
783             {
784                 AttributeType attributeType = attribute.getAttributeType();
785 
786                 // Bypass the ref attribute, unless the ManageDSAIT control is present
787                 if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
788                 {
789                     continue;
790                 }
791 
792                 entry.remove( entry.get( attributeType ) );
793             }
794 
795             entry.removeAttributes( entryDnType );
796 
797             return;
798         }
799 
800         // If the user has requested all the User attributes ('*') only, we filter the entry's attribute to keep only
801         // the USER attributes, plus the Operational attributes in the returning list 
802         if ( operationContext.isAllUserAttributes() )
803         {
804             for ( Attribute attribute : originalEntry )
805             {
806                 AttributeType attributeType = attribute.getAttributeType();
807 
808                 // Bypass the ref attribute, unless the ManageDSAIT control is present
809                 if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
810                 {
811                     continue;
812                 }
813 
814                 if ( attributeType.isOperational() )
815                 {
816                     if ( !operationContext.contains( schemaManager, attributeType ) )
817                     {
818                         entry.removeAttributes( attributeType );
819                     }
820                     else if ( typesOnly )
821                     {
822                         entry.get( attributeType ).clear();
823                     }
824                 }
825                 else if ( typesOnly )
826                 {
827                     entry.get( attributeType ).clear();
828                 }
829             }
830 
831             // DIRSERVER-1953
832             if ( !operationContext.contains( schemaManager, entryDnType ) )
833             {
834                 entry.removeAttributes( entryDnType );
835             }
836 
837             return;
838         }
839 
840         // If the user has requested all the Operational attributes ('+') only, we filter the entry's attribute to keep only
841         // the OPERATIONAL attributes, plus the User attributes in the returning list 
842         if ( operationContext.isAllOperationalAttributes() )
843         {
844             for ( Attribute attribute : originalEntry )
845             {
846                 AttributeType attributeType = attribute.getAttributeType();
847 
848                 if ( attributeType.isUser() )
849                 {
850                     if ( !operationContext.contains( schemaManager, attributeType ) )
851                     {
852                         entry.removeAttributes( attributeType );
853                     }
854                     else if ( typesOnly )
855                     {
856                         entry.get( attributeType ).clear();
857                     }
858                 }
859                 else if ( typesOnly )
860                 {
861                     entry.get( attributeType ).clear();
862                 }
863             }
864 
865             if ( !operationContext.contains( schemaManager, entryDnType ) )
866             {
867                 entry.removeAttributes( entryDnType );
868             }
869             else if ( typesOnly )
870             {
871                 entry.get( entryDnType ).clear();
872             }
873 
874             return;
875         }
876 
877         // Last, not least, check if the attributes are in the returning list
878         if ( operationContext.getReturningAttributes() != null )
879         {
880             for ( Attribute attribute : originalEntry )
881             {
882                 AttributeType attributeType = attribute.getAttributeType();
883 
884                 // Bypass the ref attribute, unless the ManageDSAIT control is present
885                 if ( operationContext.isReferralThrown() && attributeType.equals( refType ) )
886                 {
887                     continue;
888                 }
889 
890                 if ( !operationContext.contains( schemaManager, attributeType ) )
891                 {
892                     entry.removeAttributes( attributeType );
893                     continue;
894                 }
895 
896                 boolean isNotRequested = true;
897 
898                 for ( AttributeTypeOptions attrOptions : operationContext.getReturningAttributes() )
899                 {
900                     if ( attrOptions.getAttributeType().equals( attributeType )
901                         || attrOptions.getAttributeType().isAncestorOf( attributeType ) )
902                     {
903                         isNotRequested = false;
904                         break;
905                     }
906                 }
907 
908                 if ( isNotRequested )
909                 {
910                     entry.removeAttributes( attributeType );
911                 }
912                 else if ( typesOnly )
913                 {
914                     entry.get( attributeType ).clear();
915                 }
916             }
917 
918             if ( !operationContext.contains( schemaManager, entryDnType ) )
919             {
920                 entry.removeAttributes( entryDnType );
921             }
922             else if ( typesOnly )
923             {
924                 entry.get( entryDnType ).clear();
925             }
926         }
927     }
928 }