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.normalization; 021 022 023 import org.apache.directory.server.core.DirectoryService; 024 import org.apache.directory.server.core.entry.ClonedServerEntry; 025 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor; 026 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 027 import org.apache.directory.server.core.interceptor.BaseInterceptor; 028 import org.apache.directory.server.core.interceptor.NextInterceptor; 029 import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext; 030 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 031 import org.apache.directory.server.core.interceptor.context.BindOperationContext; 032 import org.apache.directory.server.core.interceptor.context.CompareOperationContext; 033 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; 034 import org.apache.directory.server.core.interceptor.context.EntryOperationContext; 035 import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext; 036 import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext; 037 import org.apache.directory.server.core.interceptor.context.ListOperationContext; 038 import org.apache.directory.server.core.interceptor.context.LookupOperationContext; 039 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 040 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext; 041 import org.apache.directory.server.core.interceptor.context.MoveOperationContext; 042 import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext; 043 import org.apache.directory.server.core.interceptor.context.RenameOperationContext; 044 import org.apache.directory.server.core.interceptor.context.SearchOperationContext; 045 import org.apache.directory.server.core.partition.DefaultPartitionNexus; 046 import org.apache.directory.shared.ldap.cursor.EmptyCursor; 047 import org.apache.directory.shared.ldap.entry.StringValue; 048 import org.apache.directory.shared.ldap.entry.ServerEntry; 049 import org.apache.directory.shared.ldap.entry.Value; 050 import org.apache.directory.shared.ldap.filter.ExprNode; 051 import org.apache.directory.shared.ldap.name.AVA; 052 import org.apache.directory.shared.ldap.name.DN; 053 import org.apache.directory.shared.ldap.name.NameComponentNormalizer; 054 import org.apache.directory.shared.ldap.name.RDN; 055 import org.apache.directory.shared.ldap.schema.AttributeType; 056 import org.apache.directory.shared.ldap.schema.SchemaManager; 057 import org.apache.directory.shared.ldap.schema.normalizers.ConcreteNameComponentNormalizer; 058 import org.slf4j.Logger; 059 import org.slf4j.LoggerFactory; 060 061 062 /** 063 * A name normalization service. This service makes sure all relative and distinguished 064 * names are normalized before calls are made against the respective interface methods 065 * on {@link DefaultPartitionNexus}. 066 * 067 * The Filters are also normalized. 068 * 069 * If the RDN AttributeTypes are not present in the entry for an Add request, 070 * they will be added. 071 * 072 * @org.apache.xbean.XBean 073 * 074 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 075 * @version $Rev: 928945 $ 076 */ 077 public class NormalizationInterceptor extends BaseInterceptor 078 { 079 /** logger used by this class */ 080 private static final Logger LOG = LoggerFactory.getLogger( NormalizationInterceptor.class ); 081 082 /** a filter node value normalizer and undefined node remover */ 083 private FilterNormalizingVisitor normVisitor; 084 085 /** The attributeType registry */ 086 private SchemaManager schemaManager; 087 088 /** 089 * Initialize the registries, normalizers. 090 */ 091 public void init( DirectoryService directoryService ) throws Exception 092 { 093 LOG.debug( "Initialiazing the NormalizationInterceptor" ); 094 095 schemaManager = directoryService.getSchemaManager(); 096 NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( schemaManager ); 097 normVisitor = new FilterNormalizingVisitor( ncn, schemaManager ); 098 } 099 100 /** 101 * The destroy method does nothing 102 */ 103 public void destroy() 104 { 105 } 106 107 // ------------------------------------------------------------------------ 108 // Normalize all Name based arguments for ContextPartition interface operations 109 // ------------------------------------------------------------------------ 110 /** 111 * {@inheritDoc} 112 */ 113 public void add( NextInterceptor nextInterceptor, AddOperationContext opContext ) throws Exception 114 { 115 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 116 opContext.getEntry().getDn().normalize( schemaManager.getNormalizerMapping() ); 117 addRdnAttributesToEntry( opContext.getDn(), opContext.getEntry() ); 118 nextInterceptor.add( opContext ); 119 } 120 121 122 /** 123 * {@inheritDoc} 124 */ 125 public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws Exception 126 { 127 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 128 nextInterceptor.delete( opContext ); 129 } 130 131 132 /** 133 * {@inheritDoc} 134 */ 135 public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext ) throws Exception 136 { 137 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 138 nextInterceptor.modify( opContext ); 139 } 140 141 142 /** 143 * {@inheritDoc} 144 */ 145 public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext ) throws Exception 146 { 147 // Normalize the new RDN and the DN 148 opContext.getNewRdn().normalize( schemaManager.getNormalizerMapping() ); 149 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 150 opContext.getNewDn().normalize( schemaManager.getNormalizerMapping() ); 151 152 // Push to the next interceptor 153 nextInterceptor.rename( opContext ); 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws Exception 161 { 162 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 163 opContext.getParent().normalize( schemaManager.getNormalizerMapping()); 164 nextInterceptor.move( opContext ); 165 } 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext ) 172 throws Exception 173 { 174 DN rdn = new DN(); 175 rdn.add( opContext.getNewRdn() ); 176 rdn.normalize( schemaManager.getNormalizerMapping() ); 177 opContext.setNewRdn( rdn.getRdn() ); 178 179 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 180 opContext.getParent().normalize( schemaManager.getNormalizerMapping() ); 181 nextInterceptor.moveAndRename( opContext ); 182 } 183 184 185 /** 186 * {@inheritDoc} 187 */ 188 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception 189 { 190 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 191 192 ExprNode filter = opContext.getFilter(); 193 194 // Normalize the filter 195 ExprNode result = ( ExprNode ) filter.accept( normVisitor ); 196 197 if ( result == null ) 198 { 199 LOG.warn( "undefined filter based on undefined attributeType not evaluted at all. Returning empty enumeration." ); 200 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext ); 201 } 202 else 203 { 204 opContext.setFilter( result ); 205 206 // TODO Normalize the returned Attributes, storing the UP attributes to format the returned values. 207 return nextInterceptor.search( opContext ); 208 } 209 } 210 211 212 /** 213 * {@inheritDoc} 214 */ 215 public boolean hasEntry( NextInterceptor nextInterceptor, EntryOperationContext opContext ) throws Exception 216 { 217 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 218 return nextInterceptor.hasEntry( opContext ); 219 } 220 221 222 /** 223 * {@inheritDoc} 224 */ 225 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception 226 { 227 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 228 return nextInterceptor.list( opContext ); 229 } 230 231 232 /** 233 * {@inheritDoc} 234 */ 235 private String[] normalizeAttrsId( String[] attrIds ) throws Exception 236 { 237 if ( attrIds == null ) 238 { 239 return attrIds; 240 } 241 242 String[] normalizedAttrIds = new String[attrIds.length]; 243 int pos = 0; 244 245 for ( String id:attrIds ) 246 { 247 String oid = schemaManager.lookupAttributeTypeRegistry( id ).getOid(); 248 normalizedAttrIds[pos++] = oid; 249 } 250 251 return normalizedAttrIds; 252 } 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws Exception 259 { 260 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 261 262 if ( opContext.getAttrsId() != null ) 263 { 264 // We have to normalize the requested IDs 265 opContext.setAttrsId( normalizeAttrsId( opContext.getAttrsIdArray() ) ); 266 } 267 268 return nextInterceptor.lookup( opContext ); 269 } 270 271 272 // ------------------------------------------------------------------------ 273 // Normalize all Name based arguments for other interface operations 274 // ------------------------------------------------------------------------ 275 /** 276 * {@inheritDoc} 277 */ 278 public DN getMatchedName ( NextInterceptor nextInterceptor, GetMatchedNameOperationContext opContext ) throws Exception 279 { 280 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 281 return nextInterceptor.getMatchedName( opContext ); 282 } 283 284 285 /** 286 * {@inheritDoc} 287 */ 288 public DN getSuffix ( NextInterceptor nextInterceptor, GetSuffixOperationContext opContext ) throws Exception 289 { 290 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 291 return nextInterceptor.getSuffix( opContext ); 292 } 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws Exception 299 { 300 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 301 302 AttributeType at = opContext.getSession().getDirectoryService().getSchemaManager().lookupAttributeTypeRegistry( opContext.getOid() ); 303 304 if ( at.getSyntax().isHumanReadable() && ( opContext.getValue().isBinary() ) ) 305 { 306 String value = opContext.getValue().getString(); 307 opContext.setValue( new StringValue( value ) ); 308 } 309 310 return next.compare( opContext ); 311 } 312 313 314 /** 315 * {@inheritDoc} 316 */ 317 public void bind( NextInterceptor next, BindOperationContext opContext ) throws Exception 318 { 319 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 320 next.bind( opContext ); 321 } 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 public void addContextPartition( NextInterceptor next, AddContextPartitionOperationContext opContext ) throws Exception 328 { 329 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 330 next.addContextPartition( opContext ); 331 } 332 333 334 /** 335 * {@inheritDoc} 336 */ 337 public void removeContextPartition( NextInterceptor next, RemoveContextPartitionOperationContext opContext ) throws Exception 338 { 339 opContext.getDn().normalize( schemaManager.getNormalizerMapping() ); 340 next.removeContextPartition( opContext ); 341 } 342 343 344 /** 345 * Adds missing RDN's attributes and values to the entry. 346 * 347 * @param dn the DN 348 * @param entry the entry 349 */ 350 private void addRdnAttributesToEntry( DN dn, ServerEntry entry ) throws Exception 351 { 352 if ( dn == null || entry == null ) 353 { 354 return; 355 } 356 357 RDN rdn = dn.getRdn(); 358 359 // Loop on all the AVAs 360 for ( AVA ava : rdn ) 361 { 362 Value<?> value = ava.getNormValue(); 363 Value<?> upValue = ava.getUpValue(); 364 String upId = ava.getUpType(); 365 366 // Check that the entry contains this AVA 367 if ( !entry.contains( upId, value ) ) 368 { 369 String message = "The RDN '" + upId + "=" + upValue + "' is not present in the entry"; 370 LOG.warn( message ); 371 372 // We don't have this attribute : add it. 373 // Two cases : 374 // 1) The attribute does not exist 375 if ( !entry.containsAttribute( upId ) ) 376 { 377 entry.add( upId, upValue ); 378 } 379 // 2) The attribute exists 380 else 381 { 382 AttributeType at = schemaManager.lookupAttributeTypeRegistry( upId ); 383 384 // 2.1 if the attribute is single valued, replace the value 385 if ( at.isSingleValued() ) 386 { 387 entry.removeAttributes( upId ); 388 entry.add( upId, upValue ); 389 } 390 // 2.2 the attribute is multi-valued : add the missing value 391 else 392 { 393 entry.add( upId, upValue ); 394 } 395 } 396 } 397 } 398 } 399 400 }