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.api.interceptor.context;
21  
22  
23  import java.util.HashSet;
24  import java.util.Set;
25  
26  import org.apache.commons.lang3.ArrayUtils;
27  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
28  import org.apache.directory.api.ldap.model.exception.LdapException;
29  import org.apache.directory.api.ldap.model.name.Dn;
30  import org.apache.directory.api.ldap.model.schema.AttributeType;
31  import org.apache.directory.api.ldap.model.schema.AttributeTypeOptions;
32  import org.apache.directory.api.ldap.model.schema.SchemaManager;
33  import org.apache.directory.api.ldap.model.schema.SchemaUtils;
34  import org.apache.directory.api.ldap.model.schema.UsageEnum;
35  import org.apache.directory.server.core.api.CoreSession;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  
40  /**
41   * A context used to store the filter used to manage the Attributes the user
42   * ha srequested. It's used by the Lookup, List and Search operations
43   *
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public abstract class FilteringOperationContext extends AbstractOperationContext
47  {
48      /** The LoggerFactory used by this Interceptor */
49      protected static final Logger LOG = LoggerFactory.getLogger( FilteringOperationContext.class );
50  
51      /** A set containing the returning attributeTypesOptions */
52      protected Set<AttributeTypeOptions> returningAttributes;
53  
54      /** The set of attributes to return as String */
55      protected String[] returningAttributesString;
56  
57      /** A flag set to true if the user has requested all the operational attributes ( "+" )*/
58      private boolean allOperationalAttributes;
59  
60      /** A flag set to true if the user has requested all the user attributes ( "*" ) */
61      private boolean allUserAttributes;
62  
63      /** A flag set to true if the user has requested no attribute to be returned (1.1) */
64      private boolean noAttributes;
65  
66      /** A flag to tell if only the attribute names to be returned. */
67      protected boolean typesOnly = false;
68  
69  
70      /**
71       * Creates a new instance of FilteringOperationContext.
72       *
73       * @param session The session to use
74       */
75      public FilteringOperationContext( CoreSession session )
76      {
77          // Default to All User Attributes if we don't have any attributes
78          this( session, SchemaConstants.ALL_USER_ATTRIBUTES );
79      }
80  
81  
82      /**
83       * Creates a new instance of FilteringOperationContext.
84       *
85       * @param session The session to use
86       * @param dn The Dn
87       */
88      public FilteringOperationContext( CoreSession session, Dn dn )
89      {
90          // Default to All User Attributes if we don't have any attributes
91          this( session, dn, SchemaConstants.ALL_USER_ATTRIBUTES );
92      }
93  
94  
95      /**
96       * Creates a new instance of LookupOperationContext.
97       *
98       * @param session The session to use
99       * @param returningAttributes The attributes to return
100      */
101     public FilteringOperationContext( CoreSession session, String... returningAttributes )
102     {
103         super( session );
104 
105         setReturningAttributes( returningAttributes );
106     }
107 
108 
109     /**
110      * Creates a new instance of LookupOperationContext.
111      *
112      * @param session The session to use
113      * @param dn The Dn
114      * @param returningAttributes The attributes to return
115      */
116     public FilteringOperationContext( CoreSession session, Dn dn, String... returningAttributes )
117     {
118         super( session, dn );
119 
120         setReturningAttributes( returningAttributes );
121     }
122 
123 
124     /**
125      * @return the returningAttributes as a Set of AttributeTypeOptions
126      */
127     public Set<AttributeTypeOptions> getReturningAttributes()
128     {
129         return returningAttributes;
130     }
131 
132 
133     /**
134      * @return the returning Attributes, as a array of Strings
135      */
136     public String[] getReturningAttributesString()
137     {
138         return returningAttributesString;
139     }
140 
141 
142     /**
143      * Tells if an attribute is present in the list of attribute to return
144      *
145      * @param schemaManager The SchemaManager instance
146      * @param attribute The attribute we are looking for
147      * @return true if the attribute is present
148      */
149     public boolean contains( SchemaManager schemaManager, String attribute )
150     {
151         if ( isNoAttributes() )
152         {
153             return false;
154         }
155 
156         try
157         {
158             AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( attribute );
159 
160             return contains( schemaManager, attributeType );
161         }
162         catch ( LdapException le )
163         {
164             return false;
165         }
166     }
167 
168 
169     /**
170      * Tells if an attribute is present in the list of attribute to return
171      *
172      * @param schemaManager The SchemaManager instance
173      * @param attributeType The attributeType we are looking for
174      * @return true if the attribute is present
175      */
176     public boolean contains( SchemaManager schemaManager, AttributeType attributeType )
177     {
178         if ( isNoAttributes() )
179         {
180             return false;
181         }
182 
183         if ( ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) && allUserAttributes )
184         {
185             return true;
186         }
187 
188         if ( ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS ) && allOperationalAttributes )
189         {
190             return true;
191         }
192 
193         // Loop on the returningAttribute, as we have two conditions to check
194         if ( returningAttributes == null )
195         {
196             return false;
197         }
198 
199         // Shortcut
200         if ( returningAttributes.contains( new AttributeTypeOptions( attributeType ) ) )
201         {
202             return true;
203         }
204 
205         // Ok, do it the slow way...
206         for ( AttributeTypeOptions attributeTypeOptions : returningAttributes )
207         {
208             if ( attributeTypeOptions.getAttributeType().equals( attributeType )
209                 || attributeTypeOptions.getAttributeType().isAncestorOf( attributeType ) )
210             {
211                 return true;
212             }
213         }
214 
215         return false;
216     }
217 
218 
219     public void setReturningAttributes( String... attributeIds )
220     {
221         if ( ( attributeIds != null ) && ( attributeIds.length != 0 ) && ( attributeIds[0] != null ) )
222         {
223             // We have something in the list
224             // first, ignore all the unkown AT and convert the strings to
225             // AttributeTypeOptions
226             returningAttributes = new HashSet<>();
227             Set<String> attributesString = new HashSet<>();
228 
229             Set<AttributeTypeOptions> collectedAttributes = collectAttributeTypes( attributeIds );
230 
231             // If we have valid, '*' or '+' attributes, we can get rid of the NoAttributes flag
232             if ( !collectedAttributes.isEmpty() || allUserAttributes || allOperationalAttributes )
233             {
234                 noAttributes = false;
235             }
236 
237             // Now, loop on the list of attributes, and remove all the USER attributes if
238             // we have the '*' attribute, and remove all the OPERATIONAL attributes if we
239             // have the '+' attribute
240             if ( !collectedAttributes.isEmpty() )
241             {
242                 for ( AttributeTypeOptions attributeTypeOption : collectedAttributes )
243                 {
244                     if ( attributeTypeOption.getAttributeType().isUser() && !allUserAttributes )
245                     {
246                         // We can add the AttributeType in the list of returningAttributeTypes
247                         returningAttributes.add( attributeTypeOption );
248                         attributesString.add( attributeTypeOption.getAttributeType().getOid() );
249                     }
250 
251                     if ( attributeTypeOption.getAttributeType().isOperational() && !allOperationalAttributes )
252                     {
253                         // We can add the AttributeType in the list of returningAttributeTypes
254                         returningAttributes.add( attributeTypeOption );
255                         attributesString.add( attributeTypeOption.getAttributeType().getOid() );
256                     }
257                 }
258             }
259 
260             if ( !attributesString.isEmpty() )
261             {
262                 // We have some valid attributes, lt's convert it to String
263                 returningAttributesString = attributesString.toArray( ArrayUtils.EMPTY_STRING_ARRAY );
264             }
265             else
266             {
267                 // No valid attributes remaining, that means they were all invalid
268                 returningAttributesString = ArrayUtils.EMPTY_STRING_ARRAY;
269             }
270         }
271         else
272         {
273             // Nothing in the list : default to '*'
274             allUserAttributes = true;
275             returningAttributesString = ArrayUtils.EMPTY_STRING_ARRAY;
276         }
277     }
278 
279 
280     private Set<AttributeTypeOptions> collectAttributeTypes( String... attributesIds )
281     {
282         Set<AttributeTypeOptions> collectedAttributes = new HashSet<>();
283 
284         if ( ( attributesIds != null ) && ( attributesIds.length != 0 ) )
285         {
286             for ( String returnAttribute : attributesIds )
287             {
288                 if ( returnAttribute == null )
289                 {
290                     continue;
291                 }
292 
293                 if ( returnAttribute.equals( SchemaConstants.NO_ATTRIBUTE ) )
294                 {
295                     noAttributes = true;
296                     continue;
297                 }
298 
299                 if ( returnAttribute.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
300                 {
301                     allOperationalAttributes = true;
302                     continue;
303                 }
304 
305                 if ( returnAttribute.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) )
306                 {
307                     allUserAttributes = true;
308                     continue;
309                 }
310 
311                 try
312                 {
313                     String id = SchemaUtils.stripOptions( returnAttribute );
314                     Set<String> options = SchemaUtils.getOptions( returnAttribute );
315 
316                     AttributeType attributeType = session.getDirectoryService()
317                         .getSchemaManager().lookupAttributeTypeRegistry( id );
318                     AttributeTypeOptions attrOptions = new AttributeTypeOptions( attributeType, options );
319 
320                     collectedAttributes.add( attrOptions );
321                 }
322                 catch ( LdapException le )
323                 {
324                     LOG.warn( "Requested attribute {} does not exist in the schema, it will be ignored",
325                         returnAttribute );
326                     // Unknown attributes should be silently ignored, as RFC 2251 states
327                 }
328             }
329         }
330 
331         return collectedAttributes;
332     }
333 
334 
335     /**
336      * @param allOperationalAttributes the allOperationalAttributes to set
337      */
338     public void setAllOperationalAttributes( boolean allOperationalAttributes )
339     {
340         this.allOperationalAttributes = allOperationalAttributes;
341     }
342 
343 
344     /**
345      * @return The flag telling if the "*" attribute has been used
346      */
347     public boolean isAllUserAttributes()
348     {
349         return allUserAttributes;
350     }
351 
352 
353     /**
354      * @param allUserAttributes the allUserAttributes to set
355      */
356     public void setAllUserAttributes( boolean allUserAttributes )
357     {
358         this.allUserAttributes = allUserAttributes;
359     }
360 
361 
362     /**
363      * @return The flag telling if the "+" attribute has been used
364      */
365     public boolean isAllOperationalAttributes()
366     {
367         return allOperationalAttributes;
368     }
369 
370 
371     /**
372      * @return The flag telling if the "1.1" attribute has been used
373      */
374     public boolean isNoAttributes()
375     {
376         return noAttributes;
377     }
378 
379 
380     /**
381      * @param noAttributes the noAttributes to set
382      */
383     public void setNoAttributes( boolean noAttributes )
384     {
385         this.noAttributes = noAttributes;
386     }
387 
388 
389     /**
390      * @return true, if attribute descriptions alone need to be returned
391      */
392     public boolean isTypesOnly()
393     {
394         return typesOnly;
395     }
396 
397 
398     /**
399      * @param typesOnly true If we want to get back the attributeType only
400      */
401     public void setTypesOnly( boolean typesOnly )
402     {
403         this.typesOnly = typesOnly;
404     }
405 
406 
407     /**
408      * @see Object#toString()
409      */
410     @Override
411     public String toString()
412     {
413         StringBuilder sb = new StringBuilder();
414 
415         sb.append( "FilteringOperationContext for Dn '" );
416         sb.append( dn.getName() ).append( "'" );
417 
418         if ( isTypesOnly() )
419         {
420             sb.append( ", type only" );
421         }
422 
423         if ( allOperationalAttributes )
424         {
425             sb.append( ", +" );
426         }
427 
428         if ( allUserAttributes )
429         {
430             sb.append( ", *" );
431         }
432 
433         if ( noAttributes )
434         {
435             sb.append( ", 1.1" );
436         }
437 
438         if ( ( returningAttributesString != null ) && ( returningAttributesString.length > 0 ) )
439         {
440             sb.append( ", attributes : <" );
441             boolean isFirst = true;
442 
443             for ( String returningAttribute : returningAttributesString )
444             {
445                 if ( isFirst )
446                 {
447                     isFirst = false;
448                 }
449                 else
450                 {
451                     sb.append( ", " );
452                 }
453 
454                 sb.append( returningAttribute );
455             }
456 
457             sb.append( ">" );
458         }
459 
460         return sb.toString();
461     }
462 }