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 }