1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.directory.server.core.trigger;
22
23
24 import org.apache.directory.server.core.DirectoryService;
25 import org.apache.directory.server.core.entry.ClonedServerEntry;
26 import org.apache.directory.server.core.entry.ServerEntry;
27 import org.apache.directory.server.core.interceptor.BaseInterceptor;
28 import org.apache.directory.server.core.interceptor.InterceptorChain;
29 import org.apache.directory.server.core.interceptor.NextInterceptor;
30 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
31 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
32 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
33 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
34 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
35 import org.apache.directory.server.core.interceptor.context.OperationContext;
36 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
37 import org.apache.directory.server.core.partition.ByPassConstants;
38 import org.apache.directory.server.core.sp.StoredProcEngine;
39 import org.apache.directory.server.core.sp.StoredProcEngineConfig;
40 import org.apache.directory.server.core.sp.StoredProcExecutionManager;
41 import org.apache.directory.server.core.sp.java.JavaStoredProcEngineConfig;
42 import org.apache.directory.server.core.subtree.SubentryInterceptor;
43 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
44 import org.apache.directory.shared.ldap.constants.SchemaConstants;
45 import org.apache.directory.shared.ldap.entry.EntryAttribute;
46 import org.apache.directory.shared.ldap.entry.Value;
47 import org.apache.directory.shared.ldap.exception.LdapNamingException;
48 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
49 import org.apache.directory.shared.ldap.name.LdapDN;
50 import org.apache.directory.shared.ldap.name.Rdn;
51 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
52 import org.apache.directory.shared.ldap.schema.OidNormalizer;
53 import org.apache.directory.shared.ldap.trigger.ActionTime;
54 import org.apache.directory.shared.ldap.trigger.LdapOperation;
55 import org.apache.directory.shared.ldap.trigger.TriggerSpecification;
56 import org.apache.directory.shared.ldap.trigger.TriggerSpecification.SPSpec;
57 import org.apache.directory.shared.ldap.trigger.TriggerSpecificationParser;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import java.text.ParseException;
62 import java.util.ArrayList;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66
67
68
69
70
71
72
73
74
75
76 public class TriggerInterceptor extends BaseInterceptor
77 {
78
79 private static final Logger LOG = LoggerFactory.getLogger( TriggerInterceptor.class );
80
81
82 private static final String ENTRY_TRIGGER_ATTR = "entryTriggerSpecification";
83
84
85 private TriggerSpecCache triggerSpecCache;
86
87
88 private TriggerSpecificationParser triggerParser;
89
90
91 private InterceptorChain chain;
92
93
94 private boolean enabled = true;
95
96
97 private TriggerExecutionAuthorizer triggerExecutionAuthorizer = new SimpleTriggerExecutionAuthorizer();
98
99 private StoredProcExecutionManager manager;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 private void addPrescriptiveTriggerSpecs( OperationContext opContext, List<TriggerSpecification> triggerSpecs,
117 LdapDN dn, ServerEntry entry ) throws Exception
118 {
119
120
121
122
123
124
125
126
127
128
129 if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
130 {
131 LdapDN parentDn = ( LdapDN ) dn.clone();
132 parentDn.remove( dn.size() - 1 );
133
134 entry = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS );
135 }
136
137 EntryAttribute subentries = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
138
139 if ( subentries == null )
140 {
141 return;
142 }
143
144 for ( Value<?> value:subentries )
145 {
146 String subentryDn = ( String ) value.get();
147 triggerSpecs.addAll( triggerSpecCache.getSubentryTriggerSpecs( subentryDn ) );
148 }
149 }
150
151
152
153
154
155
156
157
158
159
160 private void addEntryTriggerSpecs( List<TriggerSpecification> triggerSpecs, ServerEntry entry ) throws Exception
161 {
162 EntryAttribute entryTrigger = entry.get( ENTRY_TRIGGER_ATTR );
163
164 if ( entryTrigger == null )
165 {
166 return;
167 }
168
169 for ( Value<?> value:entryTrigger )
170 {
171 String triggerString = ( String ) value.get();
172 TriggerSpecification item;
173
174 try
175 {
176 item = triggerParser.parse( triggerString );
177 }
178 catch ( ParseException e )
179 {
180 String msg = "failed to parse entryTrigger: " + triggerString;
181 LOG.error( msg, e );
182 throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
183 }
184
185 triggerSpecs.add( item );
186 }
187 }
188
189
190
191
192
193
194
195
196
197
198 public Map<ActionTime, List<TriggerSpecification>> getActionTimeMappedTriggerSpecsForOperation( List<TriggerSpecification> triggerSpecs, LdapOperation ldapOperation )
199 {
200 List<TriggerSpecification> afterTriggerSpecs = new ArrayList<TriggerSpecification>();
201 Map<ActionTime, List<TriggerSpecification>> triggerSpecMap = new HashMap<ActionTime, List<TriggerSpecification>>();
202
203 for ( TriggerSpecification triggerSpec : triggerSpecs )
204 {
205 if ( triggerSpec.getLdapOperation().equals( ldapOperation ) )
206 {
207 if ( triggerSpec.getActionTime().equals( ActionTime.AFTER ) )
208 {
209 afterTriggerSpecs.add( triggerSpec );
210 }
211 else
212 {
213
214 }
215 }
216 }
217
218 triggerSpecMap.put( ActionTime.AFTER, afterTriggerSpecs );
219
220 return triggerSpecMap;
221 }
222
223
224
225
226
227 public void init( DirectoryService directoryService ) throws Exception
228 {
229 super.init( directoryService );
230
231 triggerSpecCache = new TriggerSpecCache( directoryService );
232 final AttributeTypeRegistry attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
233 triggerParser = new TriggerSpecificationParser
234 ( new NormalizerMappingResolver()
235 {
236 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception
237 {
238 return attrRegistry.getNormalizerMapping();
239 }
240 }
241 );
242 chain = directoryService.getInterceptorChain();
243
244
245 StoredProcEngineConfig javaSPEngineConfig = new JavaStoredProcEngineConfig();
246 List<StoredProcEngineConfig> spEngineConfigs = new ArrayList<StoredProcEngineConfig>();
247
248 spEngineConfigs.add( javaSPEngineConfig );
249 String spContainer = "ou=Stored Procedures,ou=system";
250 manager = new StoredProcExecutionManager( spContainer, spEngineConfigs );
251
252 this.enabled = true;
253 }
254
255
256 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception
257 {
258 LdapDN name = addContext.getDn();
259 ServerEntry entry = addContext.getEntry();
260
261
262 if ( !enabled )
263 {
264 next.add( addContext );
265 return;
266 }
267
268
269 StoredProcedureParameterInjector injector = new AddStoredProcedureParameterInjector( addContext, name, entry );
270
271
272 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
273 addPrescriptiveTriggerSpecs( addContext, triggerSpecs, name, entry );
274
275
276
277
278
279 Map<ActionTime, List<TriggerSpecification>> triggerMap
280 = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.ADD );
281
282 next.add( addContext );
283 triggerSpecCache.subentryAdded( name, entry );
284
285
286 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
287 executeTriggers( addContext, afterTriggerSpecs, injector );
288 }
289
290
291 public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws Exception
292 {
293 LdapDN name = deleteContext.getDn();
294
295
296 if ( !enabled )
297 {
298 next.delete( deleteContext );
299 return;
300 }
301
302
303 ClonedServerEntry deletedEntry = deleteContext.lookup( name , ByPassConstants.LOOKUP_BYPASS );
304
305 StoredProcedureParameterInjector injector = new DeleteStoredProcedureParameterInjector( deleteContext, name );
306
307
308 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
309 addPrescriptiveTriggerSpecs( deleteContext, triggerSpecs, name, deletedEntry );
310 addEntryTriggerSpecs( triggerSpecs, deletedEntry );
311
312 Map<ActionTime, List<TriggerSpecification>> triggerMap =
313 getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.DELETE );
314
315 next.delete( deleteContext );
316 triggerSpecCache.subentryDeleted( name, deletedEntry );
317
318
319 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
320 executeTriggers( deleteContext, afterTriggerSpecs, injector );
321 }
322
323
324 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
325 {
326
327 if ( !enabled )
328 {
329 next.modify( opContext );
330 return;
331 }
332
333 LdapDN normName = opContext.getDn();
334
335
336 ClonedServerEntry modifiedEntry = opContext.lookup( normName, ByPassConstants.LOOKUP_BYPASS );
337
338 StoredProcedureParameterInjector injector = new ModifyStoredProcedureParameterInjector( opContext );
339
340
341 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
342 addPrescriptiveTriggerSpecs( opContext, triggerSpecs, normName, modifiedEntry );
343 addEntryTriggerSpecs( triggerSpecs, modifiedEntry );
344
345 Map<ActionTime, List<TriggerSpecification>> triggerMap = getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFY );
346
347 next.modify( opContext );
348 triggerSpecCache.subentryModified( opContext, modifiedEntry );
349
350
351 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
352 executeTriggers( opContext, afterTriggerSpecs, injector );
353 }
354
355
356 public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws Exception
357 {
358 LdapDN name = renameContext.getDn();
359 Rdn newRdn = renameContext.getNewRdn();
360 boolean deleteOldRn = renameContext.getDelOldDn();
361
362
363 if ( !enabled )
364 {
365 next.rename( renameContext );
366 return;
367 }
368
369
370 ClonedServerEntry renamedEntry = renameContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
371
372 LdapDN oldRDN = new LdapDN( name.getRdn().getUpName() );
373 LdapDN oldSuperiorDN = ( LdapDN ) name.clone();
374 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
375 LdapDN newSuperiorDN = ( LdapDN ) oldSuperiorDN.clone();
376 LdapDN oldDN = ( LdapDN ) name.clone();
377 LdapDN newDN = ( LdapDN ) name.clone();
378 newDN.add( newRdn );
379
380 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
381 renameContext, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
382
383
384 List<TriggerSpecification> triggerSpecs = new ArrayList<TriggerSpecification>();
385 addPrescriptiveTriggerSpecs( renameContext, triggerSpecs, name, renamedEntry );
386 addEntryTriggerSpecs( triggerSpecs, renamedEntry );
387
388 Map<ActionTime, List<TriggerSpecification>> triggerMap =
389 getActionTimeMappedTriggerSpecsForOperation( triggerSpecs, LdapOperation.MODIFYDN_RENAME );
390
391 next.rename( renameContext );
392 triggerSpecCache.subentryRenamed( name, newDN );
393
394
395 List<TriggerSpecification> afterTriggerSpecs = triggerMap.get( ActionTime.AFTER );
396 executeTriggers( renameContext, afterTriggerSpecs, injector );
397 }
398
399
400 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
401 throws Exception
402 {
403 LdapDN oriChildName = opContext.getDn();
404 LdapDN parent = opContext.getParent();
405 Rdn newRdn = opContext.getNewRdn();
406 boolean deleteOldRn = opContext.getDelOldDn();
407
408
409 if ( !enabled )
410 {
411 next.moveAndRename( opContext );
412 return;
413 }
414
415
416 ClonedServerEntry movedEntry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
417
418 LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
419 LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
420 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
421 LdapDN newSuperiorDN = ( LdapDN ) parent.clone();
422 LdapDN oldDN = ( LdapDN ) oriChildName.clone();
423 LdapDN newDN = ( LdapDN ) parent.clone();
424 newDN.add( newRdn.getUpName() );
425
426 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
427 opContext, deleteOldRn, oldRDN, newRdn, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
428
429
430 List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
431 addPrescriptiveTriggerSpecs( opContext, exportTriggerSpecs, oriChildName, movedEntry );
432 addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
433
434
435
436
437
438
439 ClonedServerEntry importedEntry = opContext.lookup( oriChildName,
440 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
441
442
443
444
445
446
447 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
448 ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry );
449
450 for ( EntryAttribute attribute:importedEntry )
451 {
452 fakeImportedEntry.put( attribute );
453 }
454
455
456
457 List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
458 addPrescriptiveTriggerSpecs( opContext, importTriggerSpecs, newDN, fakeImportedEntry );
459
460 Map<ActionTime, List<TriggerSpecification>> exportTriggerMap =
461 getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
462
463 Map<ActionTime, List<TriggerSpecification>> importTriggerMap =
464 getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
465
466 next.moveAndRename( opContext );
467 triggerSpecCache.subentryRenamed( oldDN, newDN );
468
469
470 List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
471 List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
472 executeTriggers( opContext, afterExportTriggerSpecs, injector );
473 executeTriggers( opContext, afterImportTriggerSpecs, injector );
474 }
475
476
477 public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception
478 {
479
480 if ( !enabled )
481 {
482 next.move( opContext );
483 return;
484 }
485
486 LdapDN oriChildName = opContext.getDn();
487 LdapDN newParentName = opContext.getParent();
488
489
490 ClonedServerEntry movedEntry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
491
492 LdapDN oldRDN = new LdapDN( oriChildName.getRdn().getUpName() );
493 Rdn newRDN = new Rdn( oriChildName.getRdn().getUpName() );
494 LdapDN oldSuperiorDN = ( LdapDN ) oriChildName.clone();
495 oldSuperiorDN.remove( oldSuperiorDN.size() - 1 );
496 LdapDN newSuperiorDN = ( LdapDN ) newParentName.clone();
497 LdapDN oldDN = ( LdapDN ) oriChildName.clone();
498 LdapDN newDN = ( LdapDN ) newParentName.clone();
499 newDN.add( newRDN.getUpName() );
500
501 StoredProcedureParameterInjector injector = new ModifyDNStoredProcedureParameterInjector(
502 opContext, false, oldRDN, newRDN, oldSuperiorDN, newSuperiorDN, oldDN, newDN );
503
504
505 List<TriggerSpecification> exportTriggerSpecs = new ArrayList<TriggerSpecification>();
506 addPrescriptiveTriggerSpecs( opContext, exportTriggerSpecs, oriChildName, movedEntry );
507 addEntryTriggerSpecs( exportTriggerSpecs, movedEntry );
508
509
510
511
512
513
514 ClonedServerEntry importedEntry = opContext.lookup( oriChildName,
515 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
516
517
518
519
520
521
522 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
523 ServerEntry fakeImportedEntry = subentryInterceptor.getSubentryAttributes( newDN, importedEntry );
524
525 for ( EntryAttribute attribute:importedEntry )
526 {
527 fakeImportedEntry.put( attribute );
528 }
529
530
531
532 List<TriggerSpecification> importTriggerSpecs = new ArrayList<TriggerSpecification>();
533 addPrescriptiveTriggerSpecs( opContext, importTriggerSpecs, newDN, fakeImportedEntry );
534
535 Map<ActionTime, List<TriggerSpecification>> exportTriggerMap = getActionTimeMappedTriggerSpecsForOperation( exportTriggerSpecs, LdapOperation.MODIFYDN_EXPORT );
536
537 Map<ActionTime, List<TriggerSpecification>> importTriggerMap = getActionTimeMappedTriggerSpecsForOperation( importTriggerSpecs, LdapOperation.MODIFYDN_IMPORT );
538
539 next.move( opContext );
540 triggerSpecCache.subentryRenamed( oldDN, newDN );
541
542
543 List<TriggerSpecification> afterExportTriggerSpecs = exportTriggerMap.get( ActionTime.AFTER );
544 List<TriggerSpecification> afterImportTriggerSpecs = importTriggerMap.get( ActionTime.AFTER );
545 executeTriggers( opContext, afterExportTriggerSpecs, injector );
546 executeTriggers( opContext, afterImportTriggerSpecs, injector );
547 }
548
549
550
551
552
553
554 private Object executeTriggers( OperationContext opContext, List<TriggerSpecification> triggerSpecs,
555 StoredProcedureParameterInjector injector ) throws Exception
556 {
557 Object result = null;
558
559 for ( TriggerSpecification triggerSpec : triggerSpecs )
560 {
561
562 if ( triggerExecutionAuthorizer.hasPermission( opContext ) )
563 {
564
565
566
567
568 result = executeTrigger( opContext, triggerSpec, injector );
569 }
570 }
571
572
573
574
575
576 return result;
577 }
578
579 private Object executeTrigger( OperationContext opContext, TriggerSpecification tsec,
580 StoredProcedureParameterInjector injector ) throws Exception
581 {
582 List<Object> returnValues = new ArrayList<Object>();
583 List<SPSpec> spSpecs = tsec.getSPSpecs();
584 for ( SPSpec spSpec : spSpecs )
585 {
586 List<Object> arguments = new ArrayList<Object>();
587 arguments.addAll( injector.getArgumentsToInject( opContext, spSpec.getParameters() ) );
588 Object[] values = arguments.toArray();
589 Object returnValue = executeProcedure( opContext, spSpec.getName(), values );
590 returnValues.add( returnValue );
591 }
592
593 return returnValues;
594 }
595
596
597 private Object executeProcedure( OperationContext opContext, String procedure, Object[] values ) throws Exception
598 {
599
600 try
601 {
602 ClonedServerEntry spUnit = manager.findStoredProcUnit( opContext.getSession(), procedure );
603 StoredProcEngine engine = manager.getStoredProcEngineInstance( spUnit );
604 return engine.invokeProcedure( opContext.getSession(), procedure, values );
605 }
606 catch ( Exception e )
607 {
608 LdapNamingException lne = new LdapNamingException( ResultCodeEnum.OTHER );
609 lne.setRootCause( e );
610 throw lne;
611 }
612 }
613
614 }