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 }