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.collective;
021
022
023 import java.util.HashSet;
024 import java.util.Set;
025
026 import javax.naming.NamingException;
027
028 import org.apache.directory.server.core.DirectoryService;
029 import org.apache.directory.server.core.entry.ClonedServerEntry;
030 import org.apache.directory.server.core.filtering.EntryFilter;
031 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
032 import org.apache.directory.server.core.interceptor.BaseInterceptor;
033 import org.apache.directory.server.core.interceptor.NextInterceptor;
034 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
035 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
036 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
037 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
038 import org.apache.directory.server.core.interceptor.context.OperationContext;
039 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
040 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
041 import org.apache.directory.server.core.partition.ByPassConstants;
042 import org.apache.directory.server.core.partition.PartitionNexus;
043 import org.apache.directory.shared.ldap.constants.SchemaConstants;
044 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
045 import org.apache.directory.shared.ldap.entry.EntryAttribute;
046 import org.apache.directory.shared.ldap.entry.ServerEntry;
047 import org.apache.directory.shared.ldap.entry.Value;
048 import org.apache.directory.shared.ldap.name.DN;
049 import org.apache.directory.shared.ldap.schema.AttributeType;
050 import org.apache.directory.shared.ldap.schema.SchemaManager;
051
052
053 /**
054 * An interceptor based service dealing with collective attribute
055 * management. This service intercepts read operations on entries to
056 * inject collective attribute value pairs into the response based on
057 * the entires inclusion within collectiveAttributeSpecificAreas and
058 * collectiveAttributeInnerAreas.
059 *
060 * @org.apache.xbean.XBean
061 *
062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
063 * @version $Rev: 927380 $
064 */
065 public class CollectiveAttributeInterceptor extends BaseInterceptor
066 {
067 /** The SchemaManager */
068 private SchemaManager schemaManager;
069
070 private PartitionNexus nexus;
071
072 private CollectiveAttributesSchemaChecker collectiveAttributesSchemaChecker;
073
074
075 /**
076 * the search result filter to use for collective attribute injection
077 */
078 private final EntryFilter SEARCH_FILTER = new EntryFilter()
079 {
080 public boolean accept( SearchingOperationContext operation, ClonedServerEntry result )
081 throws Exception
082 {
083 DN name = result.getDn();
084
085 if ( name.isNormalized() == false )
086 {
087 name = DN.normalize( name, schemaManager.getNormalizerMapping() );
088 }
089
090 String[] retAttrs = operation.getSearchControls().getReturningAttributes();
091 addCollectiveAttributes( operation, result, retAttrs );
092 return true;
093 }
094 };
095
096 public void init( DirectoryService directoryService ) throws Exception
097 {
098 super.init( directoryService );
099 schemaManager = directoryService.getSchemaManager();
100 nexus = directoryService.getPartitionNexus();
101 collectiveAttributesSchemaChecker = new CollectiveAttributesSchemaChecker( nexus, schemaManager );
102 }
103
104
105 /**
106 * Adds the set of collective attributes requested in the returning attribute list
107 * and contained in subentries referenced by the entry. Excludes collective
108 * attributes that are specified to be excluded via the 'collectiveExclusions'
109 * attribute in the entry.
110 *
111 * @param opContext the context of the operation collective attributes
112 * are added to
113 * @param entry the entry to have the collective attributes injected
114 * @param retAttrs array or attribute type to be specifically included in the result entry(s)
115 * @throws NamingException if there are problems accessing subentries
116 */
117 private void addCollectiveAttributes( OperationContext opContext, ClonedServerEntry entry,
118 String[] retAttrs ) throws Exception
119 {
120 EntryAttribute collectiveAttributeSubentries =
121 entry.getOriginalEntry().get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
122
123 /*
124 * If there are no collective attribute subentries referenced then we
125 * have no collective attributes to inject to this entry.
126 */
127 if ( collectiveAttributeSubentries == null )
128 {
129 return;
130 }
131
132 /*
133 * Before we proceed we need to lookup the exclusions within the entry
134 * and build a set of exclusions for rapid lookup. We use OID values
135 * in the exclusions set instead of regular names that may have case
136 * variance.
137 */
138 EntryAttribute collectiveExclusions =
139 entry.getOriginalEntry().get( SchemaConstants.COLLECTIVE_EXCLUSIONS_AT );
140 Set<String> exclusions = new HashSet<String>();
141
142 if ( collectiveExclusions != null )
143 {
144 if ( collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT_OID )
145 ||
146 collectiveExclusions.contains( SchemaConstants.EXCLUDE_ALL_COLLECTIVE_ATTRIBUTES_AT ) )
147 {
148 /*
149 * This entry does not allow any collective attributes
150 * to be injected into itself.
151 */
152 return;
153 }
154
155 exclusions = new HashSet<String>();
156
157 for ( Value<?> value:collectiveExclusions )
158 {
159 AttributeType attrType = schemaManager.lookupAttributeTypeRegistry( value.getString() );
160 exclusions.add( attrType.getOid() );
161 }
162 }
163
164 /*
165 * If no attributes are requested specifically
166 * then it means all user attributes are requested.
167 * So populate the array with all user attributes indicator: "*".
168 */
169 if ( retAttrs == null )
170 {
171 retAttrs = SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY;
172 }
173
174 /*
175 * Construct a set of requested attributes for easier tracking.
176 */
177 Set<String> retIdsSet = new HashSet<String>( retAttrs.length );
178
179 for ( String retAttr:retAttrs )
180 {
181 if ( retAttr.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
182 retAttr.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
183 {
184 retIdsSet.add( retAttr );
185 }
186 else
187 {
188 retIdsSet.add( schemaManager.lookupAttributeTypeRegistry( retAttr ).getOid() );
189 }
190 }
191
192 /*
193 * For each collective subentry referenced by the entry we lookup the
194 * attributes of the subentry and copy collective attributes from the
195 * subentry into the entry.
196 */
197 for ( Value<?> value:collectiveAttributeSubentries )
198 {
199 String subentryDnStr = value.getString();
200 DN subentryDn = new DN( subentryDnStr );
201
202 /*
203 * TODO - Instead of hitting disk here can't we leverage the
204 * SubentryService to get us cached sub-entries so we're not
205 * wasting time with a lookup here? It is ridiculous to waste
206 * time looking up this sub-entry.
207 */
208
209 ServerEntry subentry = opContext.lookup( subentryDn, ByPassConstants.LOOKUP_COLLECTIVE_BYPASS );
210
211 for ( AttributeType attributeType:subentry.getAttributeTypes() )
212 {
213 String attrId = attributeType.getName();
214
215 if ( !attributeType.isCollective() )
216 {
217 continue;
218 }
219
220 /*
221 * Skip the addition of this collective attribute if it is excluded
222 * in the 'collectiveAttributes' attribute.
223 */
224 if ( exclusions.contains( attributeType.getOid() ) )
225 {
226 continue;
227 }
228
229 Set<AttributeType> allSuperTypes = getAllSuperTypes( attributeType );
230
231 for ( String retId : retIdsSet )
232 {
233 if ( retId.equals( SchemaConstants.ALL_USER_ATTRIBUTES ) || retId.equals( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
234 {
235 continue;
236 }
237
238 AttributeType retType = schemaManager.lookupAttributeTypeRegistry( retId );
239
240 if ( allSuperTypes.contains( retType ) )
241 {
242 retIdsSet.add( schemaManager.lookupAttributeTypeRegistry( attrId ).getOid() );
243 break;
244 }
245 }
246
247 /*
248 * If not all attributes or this collective attribute requested specifically
249 * then bypass the inclusion process.
250 */
251 if ( !( retIdsSet.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
252 retIdsSet.contains( schemaManager.lookupAttributeTypeRegistry( attrId ).getOid() ) ) )
253 {
254 continue;
255 }
256
257 EntryAttribute subentryColAttr = subentry.get( attrId );
258 EntryAttribute entryColAttr = entry.get( attrId );
259
260 /*
261 * If entry does not have attribute for collective attribute then create it.
262 */
263 if ( entryColAttr == null )
264 {
265 entryColAttr = new DefaultServerAttribute( attrId, schemaManager.lookupAttributeTypeRegistry( attrId ) );
266 entry.put( entryColAttr );
267 }
268
269 /*
270 * Add all the collective attribute values in the subentry
271 * to the currently processed collective attribute in the entry.
272 */
273 for ( Value<?> subentryColVal:subentryColAttr )
274 {
275 entryColAttr.add( subentryColVal.getString() );
276 }
277 }
278 }
279 }
280
281
282 private Set<AttributeType> getAllSuperTypes( AttributeType id ) throws Exception
283 {
284 Set<AttributeType> allSuperTypes = new HashSet<AttributeType>();
285 AttributeType superType = id;
286
287 while ( superType != null )
288 {
289 superType = superType.getSuperior();
290
291 if ( superType != null )
292 {
293 allSuperTypes.add( superType );
294 }
295 }
296
297 return allSuperTypes;
298 }
299
300
301 // ------------------------------------------------------------------------
302 // Interceptor Method Overrides
303 // ------------------------------------------------------------------------
304
305
306 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext )
307 throws Exception
308 {
309 ClonedServerEntry result = nextInterceptor.lookup( opContext );
310
311 if ( result == null )
312 {
313 return null;
314 }
315
316 if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) )
317 {
318 addCollectiveAttributes( opContext, result, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
319 }
320 else
321 {
322 addCollectiveAttributes( opContext, result, opContext.getAttrsIdArray() );
323 }
324
325 return result;
326 }
327
328
329 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception
330 {
331 EntryFilteringCursor cursor = nextInterceptor.list( opContext );
332 cursor.addEntryFilter( SEARCH_FILTER );
333 return cursor;
334 }
335
336
337 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception
338 {
339 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
340 cursor.addEntryFilter( SEARCH_FILTER );
341 return cursor;
342 }
343
344
345 // ------------------------------------------------------------------------
346 // Partial Schema Checking
347 // ------------------------------------------------------------------------
348
349
350 public void add( NextInterceptor next, AddOperationContext opContext ) throws Exception
351 {
352 collectiveAttributesSchemaChecker.checkAdd( opContext.getDn(), opContext.getEntry() );
353
354 next.add( opContext );
355 }
356
357
358 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
359 {
360 collectiveAttributesSchemaChecker.checkModify( opContext,opContext.getDn(), opContext.getModItems() );
361
362 next.modify( opContext );
363 }
364 }