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.collective;
21  
22  
23  import org.apache.directory.server.core.DirectoryService;
24  import org.apache.directory.server.core.entry.ClonedServerEntry;
25  import org.apache.directory.server.core.entry.DefaultServerAttribute;
26  import org.apache.directory.server.core.entry.ServerEntry;
27  import org.apache.directory.server.core.filtering.EntryFilter;
28  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
29  import org.apache.directory.server.core.interceptor.BaseInterceptor;
30  import org.apache.directory.server.core.interceptor.NextInterceptor;
31  import org.apache.directory.server.core.interceptor.context.AddOperationContext;
32  import org.apache.directory.server.core.interceptor.context.ListOperationContext;
33  import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
34  import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
35  import org.apache.directory.server.core.interceptor.context.OperationContext;
36  import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
37  import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
38  import org.apache.directory.server.core.partition.ByPassConstants;
39  import org.apache.directory.server.core.partition.PartitionNexus;
40  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
41  import org.apache.directory.shared.ldap.constants.SchemaConstants;
42  import org.apache.directory.shared.ldap.entry.EntryAttribute;
43  import org.apache.directory.shared.ldap.entry.Value;
44  import org.apache.directory.shared.ldap.name.LdapDN;
45  import org.apache.directory.shared.ldap.schema.AttributeType;
46  
47  import java.util.HashSet;
48  import java.util.Set;
49  
50  
51  /**
52   * An interceptor based service dealing with collective attribute
53   * management.  This service intercepts read operations on entries to
54   * inject collective attribute value pairs into the response based on
55   * the entires inclusion within collectiveAttributeSpecificAreas and
56   * collectiveAttributeInnerAreas.
57   *
58   * @org.apache.xbean.XBean
59   *
60   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
61   * @version $Rev: 664295 $
62   */
63  public class CollectiveAttributeInterceptor extends BaseInterceptor
64  {
65      /** The attributeType registry */
66      private AttributeTypeRegistry atRegistry;
67      
68      private PartitionNexus nexus;
69      
70      private CollectiveAttributesSchemaChecker collectiveAttributesSchemaChecker;
71  
72  
73      /**
74       * the search result filter to use for collective attribute injection
75       */
76      private final EntryFilter SEARCH_FILTER = new EntryFilter()
77      {
78          public boolean accept( SearchingOperationContext operation, ClonedServerEntry result )
79              throws Exception
80          {
81              LdapDN name = result.getDn();
82              
83              if ( name.isNormalized() == false )
84              {
85                  name = LdapDN.normalize( name, atRegistry.getNormalizerMapping() );
86              }
87              
88              String[] retAttrs = operation.getSearchControls().getReturningAttributes();
89              addCollectiveAttributes( operation, result, retAttrs );
90              return true;
91          }
92      };
93  
94      public void init( DirectoryService directoryService ) throws Exception
95      {
96          super.init( directoryService );
97          nexus = directoryService.getPartitionNexus();
98          atRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
99          collectiveAttributesSchemaChecker = new CollectiveAttributesSchemaChecker( nexus, atRegistry );
100     }
101 
102 
103     /**
104      * Adds the set of collective attributes requested in the returning attribute list
105      * and contained in subentries referenced by the entry. Excludes collective
106      * attributes that are specified to be excluded via the 'collectiveExclusions'
107      * attribute in the entry.
108      *
109      * @param opContext the context of the operation collective attributes 
110      * are added to
111      * @param entry the entry to have the collective attributes injected
112      * @param retAttrs array or attribute type to be specifically included in the result entry(s)
113      * @throws NamingException if there are problems accessing subentries
114      */
115     private void addCollectiveAttributes( OperationContext opContext, ClonedServerEntry entry, 
116         String[] retAttrs ) throws Exception
117     {
118         EntryAttribute collectiveAttributeSubentries = 
119             entry.getOriginalEntry().get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
120         
121         /*
122          * If there are no collective attribute subentries referenced then we 
123          * have no collective attributes to inject to this entry.
124          */
125         if ( collectiveAttributeSubentries == null )
126         {
127             return;
128         }
129     
130         /*
131          * Before we proceed we need to lookup the exclusions within the entry 
132          * and build a set of exclusions for rapid lookup.  We use OID values 
133          * in the exclusions set instead of regular names that may have case 
134          * variance.
135          */
136         EntryAttribute collectiveExclusions = 
137             entry.getOriginalEntry().get( SchemaConstants.COLLECTIVE_EXCLUSIONS_AT );
138         Set<String> exclusions = new HashSet<String>();
139         
140         if ( collectiveExclusions != null )
141         {
142             if ( collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT_OID )
143                  || 
144                  collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT  ) )
145             {
146                 /*
147                  * This entry does not allow any collective attributes
148                  * to be injected into itself.
149                  */
150                 return;
151             }
152 
153             exclusions = new HashSet<String>();
154             
155             for ( Value<?> value:collectiveExclusions )
156             {
157                 AttributeType attrType = atRegistry.lookup( ( String ) value.get() );
158                 exclusions.add( attrType.getOid() );
159             }
160         }
161         
162         /*
163          * If no attributes are requested specifically
164          * then it means all user attributes are requested.
165          * So populate the array with all user attributes indicator: "*".
166          */
167         if ( retAttrs == null )
168         {
169             retAttrs = SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY;
170         }
171         
172         /*
173          * Construct a set of requested attributes for easier tracking.
174          */ 
175         Set<String> retIdsSet = new HashSet<String>( retAttrs.length );
176         
177         for ( String retAttr:retAttrs )
178         {
179             if ( retAttr.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
180                 retAttr.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
181             {
182                 retIdsSet.add( retAttr );
183             }
184             else
185             {
186                 retIdsSet.add( atRegistry.lookup( retAttr ).getOid() );
187             }
188         }
189 
190         /*
191          * For each collective subentry referenced by the entry we lookup the
192          * attributes of the subentry and copy collective attributes from the
193          * subentry into the entry.
194          */
195         for ( Value<?> value:collectiveAttributeSubentries )
196         {
197             String subentryDnStr = ( String ) value.get();
198             LdapDN subentryDn = new LdapDN( subentryDnStr );
199             
200             /*
201              * TODO - Instead of hitting disk here can't we leverage the 
202              * SubentryService to get us cached sub-entries so we're not
203              * wasting time with a lookup here? It is ridiculous to waste
204              * time looking up this sub-entry. 
205              */
206             
207             ServerEntry subentry = opContext.lookup( subentryDn, ByPassConstants.LOOKUP_COLLECTIVE_BYPASS );
208             
209             for ( AttributeType attributeType:subentry.getAttributeTypes() )
210             {
211                 String attrId = attributeType.getName();
212                 
213                 if ( !attributeType.isCollective() )
214                 {
215                     continue;
216                 }
217                 
218                 /*
219                  * Skip the addition of this collective attribute if it is excluded
220                  * in the 'collectiveAttributes' attribute.
221                  */
222                 if ( exclusions.contains( attributeType.getOid() ) )
223                 {
224                     continue;
225                 }
226                 
227                 Set<AttributeType> allSuperTypes = getAllSuperTypes( attributeType );
228 
229                 for ( String retId : retIdsSet )
230                 {
231                     if ( retId.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) || retId.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
232                     {
233                         continue;
234                     }
235 
236                     AttributeType retType = atRegistry.lookup( retId );
237 
238                     if ( allSuperTypes.contains( retType ) )
239                     {
240                         retIdsSet.add( atRegistry.lookup( attrId ).getOid() );
241                         break;
242                     }
243                 }
244 
245                 /*
246                  * If not all attributes or this collective attribute requested specifically
247                  * then bypass the inclusion process.
248                  */
249                 if ( !( retIdsSet.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) || 
250                     retIdsSet.contains( atRegistry.lookup( attrId ).getOid() ) ) )
251                 {
252                     continue;
253                 }
254                 
255                 EntryAttribute subentryColAttr = subentry.get( attrId );
256                 EntryAttribute entryColAttr = entry.get( attrId );
257 
258                 /*
259                  * If entry does not have attribute for collective attribute then create it.
260                  */
261                 if ( entryColAttr == null )
262                 {
263                     entryColAttr = new DefaultServerAttribute( attrId, atRegistry.lookup( attrId ) );
264                     entry.put( entryColAttr );
265                 }
266 
267                 /*
268                  *  Add all the collective attribute values in the subentry
269                  *  to the currently processed collective attribute in the entry.
270                  */
271                 for ( Value<?> subentryColVal:subentryColAttr )
272                 {
273                     entryColAttr.add( (String)subentryColVal.get() );
274                 }
275             }
276         }
277     }
278     
279     
280     private Set<AttributeType> getAllSuperTypes( AttributeType id ) throws Exception
281     {
282         Set<AttributeType> allSuperTypes = new HashSet<AttributeType>();
283         AttributeType superType = id;
284         
285         while ( superType != null )
286         {
287             superType = superType.getSuperior();
288             
289             if ( superType != null )
290             {
291                 allSuperTypes.add( superType );
292             }
293         }
294         
295         return allSuperTypes;
296     }
297 
298 
299     // ------------------------------------------------------------------------
300     // Interceptor Method Overrides
301     // ------------------------------------------------------------------------
302 
303     
304     public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) 
305         throws Exception
306     {
307         ClonedServerEntry result = nextInterceptor.lookup( opContext );
308         
309         if ( result == null )
310         {
311             return null;
312         }
313         
314         if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) ) 
315         {
316             addCollectiveAttributes( opContext, result, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
317         }
318         else
319         {
320             addCollectiveAttributes( opContext, result, opContext.getAttrsIdArray() );
321         }
322 
323         return result;
324     }
325 
326 
327     public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception
328     {
329         EntryFilteringCursor cursor = nextInterceptor.list( opContext );
330         cursor.addEntryFilter( SEARCH_FILTER );
331         return cursor;
332     }
333 
334 
335     public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception
336     {
337         EntryFilteringCursor cursor = nextInterceptor.search( opContext );
338         cursor.addEntryFilter( SEARCH_FILTER );
339         return cursor;
340     }
341 
342     
343     // ------------------------------------------------------------------------
344     // Partial Schema Checking
345     // ------------------------------------------------------------------------
346     
347     
348     public void add( NextInterceptor next, AddOperationContext opContext ) throws Exception
349     {
350         collectiveAttributesSchemaChecker.checkAdd( opContext.getDn(), opContext.getEntry() );
351         
352         next.add( opContext );
353     }
354 
355 
356     public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
357     {
358         collectiveAttributesSchemaChecker.checkModify( opContext,opContext.getDn(), opContext.getModItems() );
359 
360         next.modify( opContext );
361     }
362 }