1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.exception;
21
22
23 import org.apache.commons.collections.map.LRUMap;
24 import org.apache.directory.server.core.DirectoryService;
25 import org.apache.directory.server.core.cursor.EmptyCursor;
26 import org.apache.directory.server.core.entry.ClonedServerEntry;
27 import org.apache.directory.server.core.entry.ServerAttribute;
28 import org.apache.directory.server.core.entry.ServerEntry;
29 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
30 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor;
31 import org.apache.directory.server.core.interceptor.BaseInterceptor;
32 import org.apache.directory.server.core.interceptor.NextInterceptor;
33 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
34 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
35 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
36 import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
37 import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
38 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
39 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
40 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
41 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
42 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
43 import org.apache.directory.server.core.interceptor.context.OperationContext;
44 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
45 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
46 import org.apache.directory.server.core.partition.ByPassConstants;
47 import org.apache.directory.server.core.partition.Partition;
48 import org.apache.directory.server.core.partition.PartitionNexus;
49 import org.apache.directory.shared.ldap.constants.SchemaConstants;
50 import org.apache.directory.shared.ldap.entry.EntryAttribute;
51 import org.apache.directory.shared.ldap.entry.Modification;
52 import org.apache.directory.shared.ldap.entry.ModificationOperation;
53 import org.apache.directory.shared.ldap.entry.Value;
54 import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
55 import org.apache.directory.shared.ldap.exception.LdapContextNotEmptyException;
56 import org.apache.directory.shared.ldap.exception.LdapNameAlreadyBoundException;
57 import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
58 import org.apache.directory.shared.ldap.exception.LdapNamingException;
59 import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
60 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
61 import org.apache.directory.shared.ldap.name.LdapDN;
62 import org.apache.directory.shared.ldap.schema.OidNormalizer;
63
64 import java.util.List;
65 import java.util.Map;
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class ExceptionInterceptor extends BaseInterceptor
80 {
81 private PartitionNexus nexus;
82 private DirectoryService directoryService;
83 private LdapDN subschemSubentryDn;
84
85
86
87
88
89 private Map<String, OidNormalizer> normalizerMap;
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 private final LRUMap notAliasCache = new LRUMap( DEFAULT_CACHE_SIZE );
110
111
112 private static final int DEFAULT_CACHE_SIZE = 100;
113
114
115
116
117
118 public ExceptionInterceptor()
119 {
120 }
121
122
123 public void init( DirectoryService directoryService ) throws Exception
124 {
125 this.directoryService = directoryService;
126 nexus = directoryService.getPartitionNexus();
127 normalizerMap = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
128 Value<?> attr = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
129 subschemSubentryDn = new LdapDN( ( String ) attr.get() );
130 subschemSubentryDn.normalize( normalizerMap );
131 }
132
133
134 public void destroy()
135 {
136 }
137
138
139
140
141
142 public void add( NextInterceptor nextInterceptor, AddOperationContext opContext )
143 throws Exception
144 {
145 LdapDN name = opContext.getDn();
146
147 if ( subschemSubentryDn.getNormName().equals( name.getNormName() ) )
148 {
149 throw new LdapNameAlreadyBoundException(
150 "The global schema subentry cannot be added since it exists by default." );
151 }
152
153
154 if ( nextInterceptor.hasEntry( new EntryOperationContext( opContext.getSession(), name ) ) )
155 {
156 LdapNameAlreadyBoundException ne = new LdapNameAlreadyBoundException( name.getUpName() + " already exists!" );
157 ne.setResolvedName( new LdapDN( name.getUpName() ) );
158 throw ne;
159 }
160
161 LdapDN suffix = nexus.getSuffix( new GetSuffixOperationContext( this.directoryService.getAdminSession(),
162 name ) );
163
164
165 if ( suffix.getNormName().equals( name.getNormName() ) )
166 {
167 nextInterceptor.add( opContext );
168 return;
169 }
170
171 LdapDN parentDn = ( LdapDN ) name.clone();
172 parentDn.remove( name.size() - 1 );
173
174
175 boolean notAnAlias;
176
177 synchronized( notAliasCache )
178 {
179 notAnAlias = notAliasCache.containsKey( parentDn.getNormName() );
180 }
181
182 if ( ! notAnAlias )
183 {
184
185
186 ClonedServerEntry attrs;
187
188 try
189 {
190 attrs = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS );
191 }
192 catch ( Exception e )
193 {
194 LdapNameNotFoundException e2 = new LdapNameNotFoundException( "Parent " + parentDn.getUpName()
195 + " not found" );
196 e2.setResolvedName( new LdapDN( nexus.getMatchedName(
197 new GetMatchedNameOperationContext( opContext.getSession(), parentDn ) ).getUpName() ) );
198 throw e2;
199 }
200
201 EntryAttribute objectClass = attrs.getOriginalEntry().get( SchemaConstants.OBJECT_CLASS_AT );
202
203 if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
204 {
205 String msg = "Attempt to add entry to alias '" + name.getUpName() + "' not allowed.";
206 ResultCodeEnum rc = ResultCodeEnum.ALIAS_PROBLEM;
207 LdapNamingException e = new LdapNamingException( msg, rc );
208 e.setResolvedName( new LdapDN( parentDn.getUpName() ) );
209 throw e;
210 }
211 else
212 {
213 synchronized ( notAliasCache )
214 {
215 notAliasCache.put( parentDn.getNormName(), parentDn );
216 }
217 }
218 }
219
220 nextInterceptor.add( opContext );
221 }
222
223
224
225
226
227
228 public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws Exception
229 {
230 LdapDN name = opContext.getDn();
231
232 if ( name.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
233 {
234 throw new LdapOperationNotSupportedException(
235 "Can not allow the deletion of the subschemaSubentry (" +
236 subschemSubentryDn + ") for the global schema.",
237 ResultCodeEnum.UNWILLING_TO_PERFORM );
238 }
239
240
241 String msg = "Attempt to delete non-existant entry: ";
242 assertHasEntry( nextInterceptor, opContext, msg, name );
243
244
245 boolean hasChildren = false;
246 EntryFilteringCursor list = nextInterceptor.list( new ListOperationContext( opContext.getSession(), name ) );
247
248 if ( list.next() )
249 {
250 hasChildren = true;
251 }
252
253 list.close();
254
255 if ( hasChildren )
256 {
257 LdapContextNotEmptyException e = new LdapContextNotEmptyException();
258 e.setResolvedName( new LdapDN( name.getUpName() ) );
259 throw e;
260 }
261
262 synchronized( notAliasCache )
263 {
264 if ( notAliasCache.containsKey( name.getNormName() ) )
265 {
266 notAliasCache.remove( name.getNormName() );
267 }
268 }
269
270 nextInterceptor.delete( opContext );
271 }
272
273
274
275
276
277 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception
278 {
279 if ( opContext.getDn().getNormName().equals( subschemSubentryDn.getNormName() ) )
280 {
281
282 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext );
283 }
284
285
286 String msg = "Attempt to search under non-existant entry: ";
287 assertHasEntry( nextInterceptor, opContext, msg, opContext.getDn() );
288
289 return nextInterceptor.list( opContext );
290 }
291
292
293
294
295
296 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws Exception
297 {
298 if ( opContext.getDn().getNormName().equals( subschemSubentryDn.getNormName() ) )
299 {
300 return nexus.getRootDSE( null );
301 }
302
303
304 String msg = "Attempt to lookup non-existant entry: ";
305 assertHasEntry( nextInterceptor, opContext, msg, opContext.getDn() );
306
307 return nextInterceptor.lookup( opContext );
308 }
309
310
311
312
313
314 public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
315 throws Exception
316 {
317
318 String msg = "Attempt to modify non-existant entry: ";
319
320
321
322 if ( opContext.getDn().getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
323 {
324 nextInterceptor.modify( opContext );
325 return;
326 }
327
328 assertHasEntry( nextInterceptor, opContext, msg, opContext.getDn() );
329
330 ServerEntry entry = opContext.lookup( opContext.getDn(), ByPassConstants.LOOKUP_BYPASS );
331 List<Modification> items = opContext.getModItems();
332
333 for ( Modification item : items )
334 {
335 if ( item.getOperation() == ModificationOperation.ADD_ATTRIBUTE )
336 {
337 EntryAttribute modAttr = (ServerAttribute)item.getAttribute();
338 EntryAttribute entryAttr = entry.get( modAttr.getId() );
339
340 if ( entryAttr != null )
341 {
342 for ( Value<?> value:modAttr )
343 {
344 if ( entryAttr.contains( value ) )
345 {
346 throw new LdapAttributeInUseException( "Trying to add existing value '" + value
347 + "' to attribute " + modAttr.getId() );
348 }
349 }
350 }
351 }
352 }
353
354
355
356
357
358
359 synchronized( notAliasCache )
360 {
361 if ( notAliasCache.containsKey( opContext.getDn().getNormName() ) )
362 {
363 notAliasCache.remove( opContext.getDn().getNormName() );
364 }
365 }
366
367 nextInterceptor.modify( opContext );
368 }
369
370
371
372
373 public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
374 throws Exception
375 {
376 LdapDN dn = opContext.getDn();
377
378 if ( dn.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
379 {
380 throw new LdapOperationNotSupportedException(
381 "Can not allow the renaming of the subschemaSubentry (" +
382 subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
383 ResultCodeEnum.UNWILLING_TO_PERFORM );
384 }
385
386
387 String msg = "Attempt to rename non-existant entry: ";
388 assertHasEntry( nextInterceptor, opContext, msg, dn );
389
390
391 LdapDN newDn = ( LdapDN ) dn.clone();
392 newDn.remove( dn.size() - 1 );
393 newDn.add( opContext.getNewRdn() );
394 newDn.normalize( normalizerMap );
395
396 if ( nextInterceptor.hasEntry( new EntryOperationContext( opContext.getSession(), newDn ) ) )
397 {
398 LdapNameAlreadyBoundException e;
399 e = new LdapNameAlreadyBoundException( "target entry " + newDn.getUpName() + " already exists!" );
400 e.setResolvedName( new LdapDN( newDn.getUpName() ) );
401 throw e;
402 }
403
404
405 synchronized( notAliasCache )
406 {
407 if ( notAliasCache.containsKey( dn.getNormName() ) )
408 {
409 notAliasCache.remove( dn.getNormName() );
410 }
411 }
412
413 nextInterceptor.rename( opContext );
414 }
415
416
417
418
419
420
421 public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws Exception
422 {
423 LdapDN oriChildName = opContext.getDn();
424 LdapDN newParentName = opContext.getParent();
425
426 if ( oriChildName.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
427 {
428 throw new LdapOperationNotSupportedException(
429 "Can not allow the move of the subschemaSubentry (" +
430 subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
431 ResultCodeEnum.UNWILLING_TO_PERFORM );
432 }
433
434
435 String msg = "Attempt to move to non-existant parent: ";
436 assertHasEntry( nextInterceptor, opContext, msg, oriChildName );
437
438
439 msg = "Attempt to move to non-existant parent: ";
440 assertHasEntry( nextInterceptor, opContext, msg, newParentName );
441
442
443 String rdn = oriChildName.get( oriChildName.size() - 1 );
444 LdapDN target = ( LdapDN ) newParentName.clone();
445 target.add( rdn );
446
447 if ( nextInterceptor.hasEntry( new EntryOperationContext( opContext.getSession(), target ) ) )
448 {
449
450 String upRdn = new LdapDN( oriChildName.getUpName() ).get( oriChildName.size() - 1 );
451 LdapDN upTarget = ( LdapDN ) newParentName.clone();
452 upTarget.add( upRdn );
453
454 LdapNameAlreadyBoundException e;
455 e = new LdapNameAlreadyBoundException( "target entry " + upTarget.getUpName() + " already exists!" );
456 e.setResolvedName( new LdapDN( upTarget.getUpName() ) );
457 throw e;
458 }
459
460
461 synchronized( notAliasCache )
462 {
463 if ( notAliasCache.containsKey( oriChildName.getNormName() ) )
464 {
465 notAliasCache.remove( oriChildName.getNormName() );
466 }
467 }
468
469 nextInterceptor.move( opContext );
470 }
471
472
473
474
475
476
477 public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext ) throws Exception
478 {
479 LdapDN oriChildName = opContext.getDn();
480 LdapDN parent = opContext.getParent();
481
482 if ( oriChildName.getNormName().equalsIgnoreCase( subschemSubentryDn.getNormName() ) )
483 {
484 throw new LdapOperationNotSupportedException(
485 "Can not allow the move of the subschemaSubentry (" +
486 subschemSubentryDn + ") for the global schema: it is fixed at " + subschemSubentryDn,
487 ResultCodeEnum.UNWILLING_TO_PERFORM );
488 }
489
490
491 String msg = "Attempt to move to non-existant parent: ";
492 assertHasEntry( nextInterceptor, opContext, msg, oriChildName );
493
494
495 msg = "Attempt to move to non-existant parent: ";
496 assertHasEntry( nextInterceptor, opContext, msg, parent );
497
498
499 LdapDN target = ( LdapDN ) parent.clone();
500 target.add( opContext.getNewRdn() );
501
502 if ( nextInterceptor.hasEntry( new EntryOperationContext( opContext.getSession(), target ) ) )
503 {
504
505 LdapDN upTarget = ( LdapDN ) parent.clone();
506 upTarget.add( opContext.getNewRdn() );
507
508 LdapNameAlreadyBoundException e;
509 e = new LdapNameAlreadyBoundException( "target entry " + upTarget.getUpName() + " already exists!" );
510 e.setResolvedName( new LdapDN( upTarget.getUpName() ) );
511 throw e;
512 }
513
514
515 synchronized( notAliasCache )
516 {
517 if ( notAliasCache.containsKey( oriChildName.getNormName() ) )
518 {
519 notAliasCache.remove( oriChildName.getNormName() );
520 }
521 }
522
523 nextInterceptor.moveAndRename( opContext );
524 }
525
526
527
528
529
530 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception
531 {
532 LdapDN base = opContext.getDn();
533
534 try
535 {
536 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
537
538 if ( ! cursor.next() )
539 {
540 if ( !base.isEmpty() && !( subschemSubentryDn.toNormName() ).equalsIgnoreCase( base.toNormName() ) )
541 {
542
543 assertHasEntry( nextInterceptor, opContext, "Attempt to search under non-existant entry:" , base );
544 }
545 }
546
547 return cursor;
548 }
549 catch ( Exception ne )
550 {
551 String msg = "Attempt to search under non-existant entry: ";
552 assertHasEntry( nextInterceptor, opContext, msg, base );
553 throw ne;
554 }
555 }
556
557
558
559
560
561
562
563
564
565
566
567 private void assertHasEntry( NextInterceptor nextInterceptor, OperationContext opContext,
568 String msg, LdapDN dn ) throws Exception
569 {
570 if ( subschemSubentryDn.getNormName().equals( dn.getNormName() ) )
571 {
572 return;
573 }
574
575 if ( ! opContext.hasEntry( dn, ByPassConstants.HAS_ENTRY_BYPASS ) )
576 {
577 LdapNameNotFoundException e;
578
579 if ( msg != null )
580 {
581 e = new LdapNameNotFoundException( msg + dn.getUpName() );
582 }
583 else
584 {
585 e = new LdapNameNotFoundException( dn.getUpName() );
586 }
587
588 e.setResolvedName(
589 new LdapDN(
590 opContext.getSession().getDirectoryService().getOperationManager().getMatchedName(
591 new GetMatchedNameOperationContext( opContext.getSession(), dn ) ).getUpName() ) );
592 throw e;
593 }
594 }
595 }