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.operational;
21
22
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.UUID;
29
30 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
31 import org.apache.directory.api.ldap.model.entry.Attribute;
32 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
33 import org.apache.directory.api.ldap.model.entry.DefaultModification;
34 import org.apache.directory.api.ldap.model.entry.Entry;
35 import org.apache.directory.api.ldap.model.entry.Modification;
36 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
37 import org.apache.directory.api.ldap.model.entry.Value;
38 import org.apache.directory.api.ldap.model.exception.LdapException;
39 import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException;
40 import org.apache.directory.api.ldap.model.name.Ava;
41 import org.apache.directory.api.ldap.model.name.Dn;
42 import org.apache.directory.api.ldap.model.name.Rdn;
43 import org.apache.directory.api.ldap.model.schema.AttributeType;
44 import org.apache.directory.api.ldap.model.schema.AttributeTypeOptions;
45 import org.apache.directory.api.ldap.model.schema.ObjectClass;
46 import org.apache.directory.api.ldap.model.schema.SchemaManager;
47 import org.apache.directory.api.util.DateUtils;
48 import org.apache.directory.server.constants.ApacheSchemaConstants;
49 import org.apache.directory.server.constants.ServerDNConstants;
50 import org.apache.directory.server.core.api.DirectoryService;
51 import org.apache.directory.server.core.api.InterceptorEnum;
52 import org.apache.directory.server.core.api.entry.ClonedServerEntry;
53 import org.apache.directory.server.core.api.filtering.EntryFilter;
54 import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
55 import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
56 import org.apache.directory.server.core.api.interceptor.Interceptor;
57 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
58 import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
59 import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
60 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
61 import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
62 import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
63 import org.apache.directory.server.core.api.interceptor.context.OperationContext;
64 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
65 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
66 import org.apache.directory.server.core.api.partition.Partition;
67 import org.apache.directory.server.core.api.partition.Subordinates;
68 import org.apache.directory.server.core.shared.SchemaService;
69 import org.apache.directory.server.i18n.I18n;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73
74
75
76
77
78
79
80
81
82 public class OperationalAttributeInterceptor extends BaseInterceptor
83 {
84
85 private static final Logger LOG = LoggerFactory.getLogger( OperationalAttributeInterceptor.class );
86
87
88 private final EntryFilter denormalizingSearchFilter = new OperationalAttributeDenormalizingSearchFilter();
89
90
91 private final EntryFilter operationalAttributeSearchFilter = new OperationalAttributeSearchFilter();
92
93
94 private final EntryFilter subordinatesSearchFilter = new SubordinatesSearchFilter();
95
96
97 private Dn subschemaSubentryDn;
98
99
100 private Dn adminDn;
101
102
103
104
105 private class OperationalAttributeDenormalizingSearchFilter implements EntryFilter
106 {
107
108
109
110 @Override
111 public boolean accept( SearchOperationContext operation, Entry entry ) throws LdapException
112 {
113 if ( operation.getReturningAttributesString() == null )
114 {
115 return true;
116 }
117
118
119 denormalizeEntryOpAttrs( entry );
120
121 return true;
122 }
123
124
125
126
127
128 @Override
129 public String toString( String tabs )
130 {
131 return tabs + "OperationalAttributeDenormalizingSearchFilter";
132 }
133 }
134
135
136
137
138
139 private class OperationalAttributeSearchFilter implements EntryFilter
140 {
141
142
143
144 @Override
145 public boolean accept( SearchOperationContext operation, Entry entry ) throws LdapException
146 {
147 if ( operation.getReturningAttributesString() == null )
148 {
149 return true;
150 }
151
152
153 SchemaManager schemaManager = operation.getSession().getDirectoryService().getSchemaManager();
154
155 if ( operation.isAllOperationalAttributes()
156 || operation.getReturningAttributes().contains(
157 new AttributeTypeOptions( schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) ) ) )
158 {
159 AttributeType subschemaSubentryAt = schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
160 entry.add( new DefaultAttribute( subschemaSubentryAt,
161 directoryService.getPartitionNexus().getRootDseValue( subschemaSubentryAt ) ) );
162 }
163
164 return true;
165 }
166
167
168
169
170
171 @Override
172 public String toString( String tabs )
173 {
174 return tabs + "OperationalAttributeSearchFilter";
175 }
176 }
177
178
179
180
181
182 private class SubordinatesSearchFilter implements EntryFilter
183 {
184
185
186
187 @Override
188 public boolean accept( SearchOperationContext searchOperationContext, Entry entry ) throws LdapException
189 {
190
191 processSubordinates( searchOperationContext, searchOperationContext.getReturningAttributes(),
192 searchOperationContext.isAllOperationalAttributes(), entry );
193
194 return true;
195 }
196
197
198
199
200
201 @Override
202 public String toString( String tabs )
203 {
204 return tabs + "SubordinatesSearchFilter";
205 }
206 }
207
208
209
210
211
212 public OperationalAttributeInterceptor()
213 {
214 super( InterceptorEnum.OPERATIONAL_ATTRIBUTE_INTERCEPTOR );
215 }
216
217
218 @Override
219 public void init( DirectoryService directoryService ) throws LdapException
220 {
221 super.init( directoryService );
222
223
224 Value subschemaSubentry = directoryService.getPartitionNexus().getRootDseValue(
225 directoryService.getAtProvider().getSubschemaSubentry() );
226 subschemaSubentryDn = dnFactory.create( subschemaSubentry.getString() );
227
228
229 adminDn = dnFactory.create( ServerDNConstants.ADMIN_SYSTEM_DN );
230 }
231
232
233 @Override
234 public void destroy()
235 {
236 }
237
238
239
240
241
242 private boolean checkAddOperationalAttribute( boolean isAdmin, Entry entry, AttributeType attribute )
243 throws LdapException
244 {
245 if ( entry.containsAttribute( attribute ) )
246 {
247 if ( !isAdmin )
248 {
249
250 String message = I18n.err( I18n.ERR_30, attribute );
251 LOG.error( message );
252 throw new LdapNoPermissionException( message );
253 }
254 else
255 {
256 return true;
257 }
258 }
259 else
260 {
261 return false;
262 }
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 @Override
279 public void add( AddOperationContext addContext ) throws LdapException
280 {
281 String principal = getPrincipal( addContext ).getName();
282
283 Entry entry = addContext.getEntry();
284
285
286
287 boolean isAdmin = addContext.getSession().getAuthenticatedPrincipal().getDn().equals( adminDn );
288
289
290 if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getEntryUUID() ) )
291 {
292 entry.put( directoryService.getAtProvider().getEntryUUID(), UUID.randomUUID().toString() );
293 }
294
295
296 if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getEntryCSN() ) )
297 {
298 entry.put( directoryService.getAtProvider().getEntryCSN(), directoryService.getCSN().toString() );
299 }
300
301
302 if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getCreatorsName() ) )
303 {
304 entry.put( directoryService.getAtProvider().getCreatorsName(), principal );
305 }
306
307
308 if ( !checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getCreateTimestamp() ) )
309 {
310 entry.put( directoryService.getAtProvider().getCreateTimestamp(), DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
311 }
312
313
314
315 checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getAccessControlSubentries() );
316
317
318 checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider()
319 .getCollectiveAttributeSubentries() );
320
321
322 checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getTriggerExecutionSubentries() );
323
324
325 checkAddOperationalAttribute( isAdmin, entry, directoryService.getAtProvider().getSubschemaSubentry() );
326
327 next( addContext );
328 }
329
330
331
332
333
334 @Override
335 public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
336 {
337 Dn dn = lookupContext.getDn();
338
339 if ( dn.getNormName().equals( subschemaSubentryDn.getNormName() ) )
340 {
341 Entry serverEntry = SchemaService.getSubschemaEntry( directoryService, lookupContext );
342 serverEntry.setDn( dn );
343
344 return serverEntry;
345 }
346
347 Entry entry = next( lookupContext );
348
349 denormalizeEntryOpAttrs( entry );
350
351
352 processSubordinates( lookupContext, lookupContext.getReturningAttributes(), lookupContext.isAllOperationalAttributes(), entry );
353
354 return entry;
355 }
356
357
358
359
360
361 @Override
362 public void modify( ModifyOperationContext modifyContext ) throws LdapException
363 {
364
365
366
367
368
369 List<Modification> mods = modifyContext.getModItems();
370
371 boolean isAdmin = modifyContext.getSession().getAuthenticatedPrincipal().getDn().equals( adminDn );
372
373 boolean modifierAtPresent = false;
374 boolean modifiedTimeAtPresent = false;
375 boolean entryCsnAtPresent = false;
376 Dn dn = modifyContext.getDn();
377
378 for ( Modification modification : mods )
379 {
380 AttributeType attributeType = modification.getAttribute().getAttributeType();
381
382 if ( attributeType.equals( directoryService.getAtProvider().getModifiersName() ) )
383 {
384 if ( !isAdmin )
385 {
386 String message = I18n.err( I18n.ERR_31 );
387 LOG.error( message );
388 throw new LdapNoPermissionException( message );
389 }
390 else
391 {
392 modifierAtPresent = true;
393 }
394 }
395
396 if ( attributeType.equals( directoryService.getAtProvider().getModifyTimestamp() ) )
397 {
398 if ( !isAdmin )
399 {
400 String message = I18n.err( I18n.ERR_30, attributeType );
401 LOG.error( message );
402 throw new LdapNoPermissionException( message );
403 }
404 else
405 {
406 modifiedTimeAtPresent = true;
407 }
408 }
409
410 if ( attributeType.equals( directoryService.getAtProvider().getEntryCSN() ) )
411 {
412 if ( !isAdmin )
413 {
414 String message = I18n.err( I18n.ERR_30, attributeType );
415 LOG.error( message );
416 throw new LdapNoPermissionException( message );
417 }
418 else
419 {
420 entryCsnAtPresent = true;
421 }
422 }
423
424 if ( PWD_POLICY_STATE_ATTRIBUTE_TYPES.contains( attributeType ) && !isAdmin )
425 {
426 String message = I18n.err( I18n.ERR_30, attributeType );
427 LOG.error( message );
428 throw new LdapNoPermissionException( message );
429 }
430 }
431
432
433 if ( !dn.equals( subschemaSubentryDn ) )
434 {
435 if ( !modifierAtPresent )
436 {
437
438 Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getModifiersName(),
439 getPrincipal( modifyContext ).getName() );
440
441 Modification modifiersName = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
442 attribute );
443
444 mods.add( modifiersName );
445 }
446
447 if ( !modifiedTimeAtPresent )
448 {
449
450 Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getModifyTimestamp(),
451 DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
452
453 Modification timestamp = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
454
455 mods.add( timestamp );
456 }
457
458 if ( !entryCsnAtPresent )
459 {
460 String csn = directoryService.getCSN().toString();
461 Attribute attribute = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), csn );
462 Modification updatedCsn = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
463 mods.add( updatedCsn );
464 }
465 }
466
467
468 next( modifyContext );
469 }
470
471
472
473
474
475 @Override
476 public void move( MoveOperationContext moveContext ) throws LdapException
477 {
478 Entry modifiedEntry = moveContext.getOriginalEntry().clone();
479 modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( moveContext ).getName() );
480 modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
481
482 Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService
483 .getCSN().toString() );
484 modifiedEntry.put( csnAt );
485
486 modifiedEntry.setDn( moveContext.getNewDn() );
487 moveContext.setModifiedEntry( modifiedEntry );
488
489 next( moveContext );
490 }
491
492
493
494
495
496 @Override
497 public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
498 {
499 Entry modifiedEntry = moveAndRenameContext.getModifiedEntry();
500 modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( moveAndRenameContext ).getName() );
501 modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
502 modifiedEntry.setDn( moveAndRenameContext.getNewDn() );
503
504 Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService
505 .getCSN().toString() );
506 modifiedEntry.put( csnAt );
507
508 moveAndRenameContext.setModifiedEntry( modifiedEntry );
509
510 next( moveAndRenameContext );
511 }
512
513
514
515
516
517 @Override
518 public void rename( RenameOperationContext renameContext ) throws LdapException
519 {
520 Entry entry = ( ( ClonedServerEntry ) renameContext.getEntry() ).getClonedEntry();
521 entry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( renameContext ).getName() );
522 entry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
523
524 Entry modifiedEntry = renameContext.getOriginalEntry().clone();
525 modifiedEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal( renameContext ).getName() );
526 modifiedEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
527
528 Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService
529 .getCSN().toString() );
530 modifiedEntry.put( csnAt );
531
532 renameContext.setModifiedEntry( modifiedEntry );
533
534 next( renameContext );
535 }
536
537
538
539
540
541 @Override
542 public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
543 {
544 EntryFilteringCursor cursor = next( searchContext );
545
546 if ( searchContext.isAllOperationalAttributes()
547 || ( ( searchContext.getReturningAttributes() != null ) && !searchContext.getReturningAttributes().isEmpty() ) )
548 {
549 if ( directoryService.isDenormalizeOpAttrsEnabled() )
550 {
551 cursor.addEntryFilter( denormalizingSearchFilter );
552 }
553
554 cursor.addEntryFilter( operationalAttributeSearchFilter );
555 cursor.addEntryFilter( subordinatesSearchFilter );
556
557 return cursor;
558 }
559
560 return cursor;
561 }
562
563
564 @Override
565 public void delete( DeleteOperationContext deleteContext ) throws LdapException
566 {
567
568 Entry entry = deleteContext.getEntry();
569 Attribute csnAt = new DefaultAttribute( directoryService.getAtProvider().getEntryCSN(), directoryService
570 .getCSN().toString() );
571 entry.put( csnAt );
572
573 next( deleteContext );
574 }
575
576
577 private void denormalizeEntryOpAttrs( Entry entry ) throws LdapException
578 {
579 if ( directoryService.isDenormalizeOpAttrsEnabled() )
580 {
581 Attribute attr = entry.get( SchemaConstants.CREATORS_NAME_AT );
582
583 if ( attr != null )
584 {
585 Dn creatorsName = dnFactory.create( attr.getString() );
586
587 attr.clear();
588 attr.add( denormalizeTypes( creatorsName ).getName() );
589 }
590
591 attr = entry.get( SchemaConstants.MODIFIERS_NAME_AT );
592
593 if ( attr != null )
594 {
595 Dn modifiersName = dnFactory.create( attr.getString() );
596
597 attr.clear();
598 attr.add( denormalizeTypes( modifiersName ).getName() );
599 }
600
601 attr = entry.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
602
603 if ( attr != null )
604 {
605 Dn modifiersName = dnFactory.create( attr.getString() );
606
607 attr.clear();
608 attr.add( denormalizeTypes( modifiersName ).getName() );
609 }
610 }
611 }
612
613
614
615
616
617
618
619
620
621
622 private Dn denormalizeTypes( Dn dn ) throws LdapException
623 {
624 Dn newDn = new Dn( schemaManager );
625 int size = dn.size();
626
627 for ( int pos = 0; pos < size; pos++ )
628 {
629 Rdn rdn = dn.getRdn( size - 1 - pos );
630
631 if ( rdn.size() == 0 )
632 {
633 newDn = newDn.add( new Rdn() );
634 continue;
635 }
636 else if ( rdn.size() == 1 )
637 {
638 String name = schemaManager.lookupAttributeTypeRegistry( rdn.getNormType() ).getName();
639 String value = rdn.getValue();
640 newDn = newDn.add( new Rdn( name, value ) );
641 continue;
642 }
643
644
645 StringBuilder buf = new StringBuilder();
646
647 for ( Iterator<Ava> atavs = rdn.iterator(); atavs.hasNext(); )
648 {
649 Ava atav = atavs.next();
650 String type = schemaManager.lookupAttributeTypeRegistry( rdn.getNormType() ).getName();
651 buf.append( type ).append( '=' ).append( atav.getValue().getString() );
652
653 if ( atavs.hasNext() )
654 {
655 buf.append( '+' );
656 }
657 }
658
659 newDn = newDn.add( new Rdn( buf.toString() ) );
660 }
661
662 return newDn;
663 }
664
665
666 private void processSubordinates( OperationContext operationContext, Set<AttributeTypeOptions> returningAttributes,
667 boolean allAttributes, Entry entry ) throws LdapException
668 {
669
670 if ( Dn.isNullOrEmpty( entry.getDn() ) )
671 {
672 return;
673 }
674
675
676 AttributeType nbChildrenAt = directoryService.getAtProvider().getNbChildren();
677 AttributeTypeOptions nbChildrenAto = new AttributeTypeOptions( nbChildrenAt );
678 AttributeType nbSubordinatesAt = directoryService.getAtProvider().getNbSubordinates();
679 AttributeTypeOptions nbSubordinatesAto = new AttributeTypeOptions( nbSubordinatesAt );
680 AttributeType hasSubordinatesAt = directoryService.getAtProvider().getHasSubordinates();
681 AttributeTypeOptions hasSubordinatesAto = new AttributeTypeOptions( hasSubordinatesAt );
682 AttributeType structuralObjectClassAt = directoryService.getAtProvider().getStructuralObjectClass();
683 AttributeTypeOptions structuralObjectClassAto = new AttributeTypeOptions( structuralObjectClassAt );
684
685 if ( returningAttributes != null )
686 {
687 boolean nbChildrenRequested = returningAttributes.contains( nbChildrenAto ) || allAttributes;
688 boolean nbSubordinatesRequested = returningAttributes.contains( nbSubordinatesAto ) || allAttributes;
689 boolean hasSubordinatesRequested = returningAttributes.contains( hasSubordinatesAto ) || allAttributes;
690 boolean structuralObjectClassRequested = returningAttributes.contains( structuralObjectClassAto ) || allAttributes;
691
692 if ( nbChildrenRequested || nbSubordinatesRequested || hasSubordinatesRequested
693 || structuralObjectClassRequested )
694 {
695 Partition partition = directoryService.getPartitionNexus().getPartition( entry.getDn() );
696 Subordinates subordinates = partition.getSubordinates( operationContext.getTransaction(), entry );
697
698 long nbChildren = subordinates.getNbChildren();
699 long nbSubordinates = subordinates.getNbSubordinates();
700
701
702 if ( nbChildrenRequested )
703 {
704 entry.add( new DefaultAttribute( nbChildrenAt,
705 Long.toString( nbChildren ) ) );
706 }
707
708
709 if ( nbSubordinatesRequested )
710 {
711 entry.add( new DefaultAttribute( nbSubordinatesAt,
712 Long.toString( nbSubordinates ) ) );
713 }
714
715
716 if ( hasSubordinatesRequested )
717 {
718 if ( nbSubordinates > 0 )
719 {
720 entry.add( new DefaultAttribute( hasSubordinatesAt, "TRUE" ) );
721 }
722 else
723 {
724 entry.add( new DefaultAttribute( hasSubordinatesAt, "FALSE" ) );
725 }
726 }
727
728
729 if ( structuralObjectClassRequested )
730 {
731 Attribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
732 Map<String, ObjectClass> superiors = new HashMap<>();
733 ObjectClass[] objectClassArray = new ObjectClass[objectClasses.size()];
734 int nbStructural = 0;
735
736
737 for ( Value objectClassValue : objectClasses )
738 {
739 ObjectClass objectClass =
740 schemaManager.getObjectClassRegistry().get( objectClassValue.getNormalized() );
741
742 if ( objectClass.isStructural() )
743 {
744 objectClassArray[nbStructural++] = objectClass;
745
746
747 superiors.put( objectClass.getSuperiors().get( 0 ).getOid(), objectClass );
748 }
749 }
750
751
752 if ( nbStructural == 1 )
753 {
754 entry.add( new DefaultAttribute( structuralObjectClassAt,
755 objectClassArray[0].getName() ) );
756 }
757 else
758 {
759 ObjectClass topStructural = objectClassArray[0];
760
761 for ( ObjectClass oc : objectClassArray )
762 {
763 if ( !superiors.containsKey( oc.getOid() ) )
764 {
765
766
767 entry.add( new DefaultAttribute( structuralObjectClassAt, oc.getName() ) );
768 break;
769 }
770 }
771 }
772 }
773 }
774 }
775 }
776 }