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.schema;
21
22
23 import java.io.UnsupportedEncodingException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32 import javax.naming.NamingException;
33 import javax.naming.NoPermissionException;
34 import javax.naming.directory.InvalidAttributeValueException;
35 import javax.naming.directory.SearchControls;
36
37 import org.apache.directory.server.constants.MetaSchemaConstants;
38 import org.apache.directory.server.constants.ServerDNConstants;
39 import org.apache.directory.server.core.DirectoryService;
40 import org.apache.directory.server.core.cursor.EmptyCursor;
41 import org.apache.directory.server.core.cursor.SingletonCursor;
42 import org.apache.directory.server.core.entry.ClonedServerEntry;
43 import org.apache.directory.server.core.entry.DefaultServerAttribute;
44 import org.apache.directory.server.core.entry.ServerAttribute;
45 import org.apache.directory.server.core.entry.ServerBinaryValue;
46 import org.apache.directory.server.core.entry.ServerEntry;
47 import org.apache.directory.server.core.entry.ServerStringValue;
48 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor;
49 import org.apache.directory.server.core.filtering.EntryFilter;
50 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
51 import org.apache.directory.server.core.interceptor.BaseInterceptor;
52 import org.apache.directory.server.core.interceptor.NextInterceptor;
53 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
54 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
55 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
56 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
57 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
58 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
59 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
60 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
61 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
62 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
63 import org.apache.directory.server.core.partition.ByPassConstants;
64 import org.apache.directory.server.core.partition.PartitionNexus;
65 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
66 import org.apache.directory.server.schema.registries.ObjectClassRegistry;
67 import org.apache.directory.server.schema.registries.OidRegistry;
68 import org.apache.directory.server.schema.registries.Registries;
69 import org.apache.directory.shared.ldap.constants.SchemaConstants;
70 import org.apache.directory.shared.ldap.entry.Entry;
71 import org.apache.directory.shared.ldap.entry.EntryAttribute;
72 import org.apache.directory.shared.ldap.entry.Modification;
73 import org.apache.directory.shared.ldap.entry.ModificationOperation;
74 import org.apache.directory.shared.ldap.entry.Value;
75 import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
76 import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
77 import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
78 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeIdentifierException;
79 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
80 import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
81 import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
82 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
83 import org.apache.directory.shared.ldap.filter.ApproximateNode;
84 import org.apache.directory.shared.ldap.filter.AssertionNode;
85 import org.apache.directory.shared.ldap.filter.BranchNode;
86 import org.apache.directory.shared.ldap.filter.EqualityNode;
87 import org.apache.directory.shared.ldap.filter.ExprNode;
88 import org.apache.directory.shared.ldap.filter.ExtensibleNode;
89 import org.apache.directory.shared.ldap.filter.GreaterEqNode;
90 import org.apache.directory.shared.ldap.filter.LessEqNode;
91 import org.apache.directory.shared.ldap.filter.PresenceNode;
92 import org.apache.directory.shared.ldap.filter.ScopeNode;
93 import org.apache.directory.shared.ldap.filter.SimpleNode;
94 import org.apache.directory.shared.ldap.filter.SubstringNode;
95 import org.apache.directory.shared.ldap.message.CascadeControl;
96 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
97 import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
98 import org.apache.directory.shared.ldap.name.LdapDN;
99 import org.apache.directory.shared.ldap.name.Rdn;
100 import org.apache.directory.shared.ldap.schema.AttributeType;
101 import org.apache.directory.shared.ldap.schema.AttributeTypeOptions;
102 import org.apache.directory.shared.ldap.schema.ObjectClass;
103 import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
104 import org.apache.directory.shared.ldap.schema.SchemaUtils;
105 import org.apache.directory.shared.ldap.schema.UsageEnum;
106 import org.apache.directory.shared.ldap.schema.syntax.AcceptAllSyntaxChecker;
107 import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
108 import org.apache.directory.shared.ldap.util.StringTools;
109 import org.slf4j.Logger;
110 import org.slf4j.LoggerFactory;
111
112
113
114
115
116
117
118
119
120
121
122
123 public class SchemaInterceptor extends BaseInterceptor
124 {
125
126 private static Logger LOG = LoggerFactory.getLogger( SchemaInterceptor.class );
127
128 private static final String[] SCHEMA_SUBENTRY_RETURN_ATTRIBUTES = new String[]
129 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES };
130
131
132 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
133
134
135
136
137 private PartitionNexus nexus;
138
139
140
141
142 private BinaryAttributeFilter binaryAttributeFilter;
143
144 private TopFilter topFilter;
145
146 private List<EntryFilter> filters = new ArrayList<EntryFilter>();
147
148
149
150
151 private Registries registries;
152
153
154 private AttributeType OBJECT_CLASS;
155
156
157
158 private AttributeTypeRegistry atRegistry;
159
160
161 private String subschemaSubentryDnNorm;
162
163
164
165
166 private LdapDN schemaModificationAttributesDN;
167
168 private SchemaOperationControl schemaManager;
169
170 private SchemaService schemaService;
171
172
173 private LdapDN schemaBaseDN;
174
175
176 private Map<String, List<ObjectClass>> superiors;
177
178
179 private Map<String, List<AttributeType>> allMay;
180
181
182 private Map<String, List<AttributeType>> allMust;
183
184
185 private Map<String, List<AttributeType>> allowed;
186
187
188
189
190
191
192
193
194 public void init( DirectoryService directoryService ) throws Exception
195 {
196 if ( IS_DEBUG )
197 {
198 LOG.debug( "Initializing SchemaInterceptor..." );
199 }
200
201 nexus = directoryService.getPartitionNexus();
202 registries = directoryService.getRegistries();
203 atRegistry = registries.getAttributeTypeRegistry();
204 OBJECT_CLASS = atRegistry.lookup( SchemaConstants.OBJECT_CLASS_AT );
205 binaryAttributeFilter = new BinaryAttributeFilter();
206 topFilter = new TopFilter();
207 filters.add( binaryAttributeFilter );
208 filters.add( topFilter );
209
210 schemaBaseDN = new LdapDN( ServerDNConstants.OU_SCHEMA_DN );
211 schemaBaseDN.normalize( atRegistry.getNormalizerMapping() );
212 schemaService = directoryService.getSchemaService();
213 schemaManager = directoryService.getSchemaService().getSchemaControl();
214
215
216 Value<?> subschemaSubentry = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
217 LdapDN subschemaSubentryDn = new LdapDN( ( String ) ( subschemaSubentry.get() ) );
218 subschemaSubentryDn.normalize( atRegistry.getNormalizerMapping() );
219 subschemaSubentryDnNorm = subschemaSubentryDn.getNormName();
220
221 schemaModificationAttributesDN = new LdapDN( "cn=schemaModifications,ou=schema" );
222 schemaModificationAttributesDN.normalize( atRegistry.getNormalizerMapping() );
223
224 computeSuperiors();
225
226 if ( IS_DEBUG )
227 {
228 LOG.debug( "SchemaInterceptor Initialized !" );
229 }
230 }
231
232
233
234
235
236
237
238
239
240
241 private void computeMustAttributes( ObjectClass objectClass, Set<String> atSeen ) throws Exception
242 {
243 List<ObjectClass> parents = superiors.get( objectClass.getOid() );
244
245 List<AttributeType> mustList = new ArrayList<AttributeType>();
246 List<AttributeType> allowedList = new ArrayList<AttributeType>();
247 Set<String> mustSeen = new HashSet<String>();
248
249 allMust.put( objectClass.getOid(), mustList );
250 allowed.put( objectClass.getOid(), allowedList );
251
252 for ( ObjectClass parent : parents )
253 {
254 AttributeType[] mustParent = parent.getMustList();
255
256 if ( ( mustParent != null ) && ( mustParent.length != 0 ) )
257 {
258 for ( AttributeType attributeType : mustParent )
259 {
260 String oid = attributeType.getOid();
261
262 if ( !mustSeen.contains( oid ) )
263 {
264 mustSeen.add( oid );
265 mustList.add( attributeType );
266 allowedList.add( attributeType );
267 atSeen.add( attributeType.getOid() );
268 }
269 }
270 }
271 }
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285 private void computeMayAttributes( ObjectClass objectClass, Set<String> atSeen ) throws Exception
286 {
287 List<ObjectClass> parents = superiors.get( objectClass.getOid() );
288
289 List<AttributeType> mayList = new ArrayList<AttributeType>();
290 Set<String> maySeen = new HashSet<String>();
291 List<AttributeType> allowedList = allowed.get( objectClass.getOid() );
292
293 allMay.put( objectClass.getOid(), mayList );
294
295 for ( ObjectClass parent : parents )
296 {
297 AttributeType[] mustParent = parent.getMustList();
298
299 if ( ( mustParent != null ) && ( mustParent.length != 0 ) )
300 {
301 for ( AttributeType attributeType : mustParent )
302 {
303 String oid = attributeType.getOid();
304
305 if ( !maySeen.contains( oid ) )
306 {
307 maySeen.add( oid );
308 mayList.add( attributeType );
309
310 if ( !atSeen.contains( oid ) )
311 {
312 allowedList.add( attributeType );
313 }
314 }
315 }
316 }
317 }
318 }
319
320
321
322
323
324
325
326
327
328
329 private void computeOCSuperiors( ObjectClass objectClass, List<ObjectClass> superiors, Set<String> ocSeen )
330 throws Exception
331 {
332 ObjectClass[] parents = objectClass.getSuperClasses();
333
334
335 if ( ( parents != null ) && ( parents.length != 0 ) )
336 {
337 for ( ObjectClass parent : parents )
338 {
339
340 if ( SchemaConstants.TOP_OC.equals( parent.getName() ) )
341 {
342 continue;
343 }
344
345
346 computeOCSuperiors( parent, superiors, ocSeen );
347
348 String oid = parent.getOid();
349
350 if ( !ocSeen.contains( oid ) )
351 {
352 superiors.add( parent );
353 ocSeen.add( oid );
354 }
355 }
356 }
357 }
358
359
360
361
362
363
364 private void computeSuperiors() throws Exception
365 {
366 Iterator<ObjectClass> objectClasses = registries.getObjectClassRegistry().iterator();
367 superiors = new HashMap<String, List<ObjectClass>>();
368 allMust = new HashMap<String, List<AttributeType>>();
369 allMay = new HashMap<String, List<AttributeType>>();
370 allowed = new HashMap<String, List<AttributeType>>();
371
372 while ( objectClasses.hasNext() )
373 {
374 List<ObjectClass> ocSuperiors = new ArrayList<ObjectClass>();
375
376 ObjectClass objectClass = objectClasses.next();
377 superiors.put( objectClass.getOid(), ocSuperiors );
378
379 computeOCSuperiors( objectClass, ocSuperiors, new HashSet<String>() );
380
381 Set<String> atSeen = new HashSet<String>();
382 computeMustAttributes( objectClass, atSeen );
383 computeMayAttributes( objectClass, atSeen );
384
385 superiors.put( objectClass.getName(), ocSuperiors );
386 }
387 }
388
389
390 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext )
391 throws Exception
392 {
393 EntryFilteringCursor cursor = nextInterceptor.list( opContext );
394 cursor.addEntryFilter( binaryAttributeFilter );
395 return cursor;
396 }
397
398
399
400
401
402
403
404
405
406
407
408
409
410 private void filterAttributesToReturn( SearchControls searchCtls )
411 {
412 String[] attributes = searchCtls.getReturningAttributes();
413
414 if ( ( attributes == null ) || ( attributes.length == 0 ) )
415 {
416
417 searchCtls.setReturningAttributes( SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY );
418 return;
419 }
420
421 Map<String, String> filteredAttrs = new HashMap<String, String>();
422 boolean hasNoAttribute = false;
423 boolean hasAttributes = false;
424
425 for ( String attribute : attributes )
426 {
427
428 if ( ( SchemaConstants.ALL_USER_ATTRIBUTES.equals( attribute ) )
429 || ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( attribute ) )
430 || ( SchemaConstants.NO_ATTRIBUTE.equals( attribute ) ) )
431 {
432 if ( !filteredAttrs.containsKey( attribute ) )
433 {
434 filteredAttrs.put( attribute, attribute );
435 }
436
437 if ( SchemaConstants.NO_ATTRIBUTE.equals( attribute ) )
438 {
439 hasNoAttribute = true;
440 }
441 else
442 {
443 hasAttributes = true;
444 }
445
446 continue;
447 }
448
449 try
450 {
451
452 if ( registries.getOidRegistry().hasOid( attribute ) )
453 {
454 String oid = registries.getOidRegistry().getOid( attribute );
455
456
457 if ( atRegistry.hasAttributeType( oid ) )
458 {
459 if ( !filteredAttrs.containsKey( oid ) )
460 {
461
462 filteredAttrs.put( oid, attribute );
463 }
464 }
465 }
466
467 hasAttributes = true;
468 }
469 catch ( Exception ne )
470 {
471
472 }
473 }
474
475
476 if ( hasAttributes && hasNoAttribute )
477 {
478 filteredAttrs.remove( SchemaConstants.NO_ATTRIBUTE );
479 }
480
481
482 if ( filteredAttrs.size() == attributes.length )
483 {
484 return;
485 }
486
487
488 if ( filteredAttrs.size() == 0 )
489 {
490
491
492 searchCtls.setReturningAttributes( SchemaConstants.NO_ATTRIBUTE_ARRAY );
493 return;
494 }
495
496
497 String[] newAttributesList = new String[filteredAttrs.size()];
498
499 int pos = 0;
500
501 for ( String key : filteredAttrs.keySet() )
502 {
503 newAttributesList[pos++] = filteredAttrs.get( key );
504 }
505
506 searchCtls.setReturningAttributes( newAttributesList );
507 }
508
509
510 private Value<?> convert( String id, Object value ) throws Exception
511 {
512 AttributeType at = atRegistry.lookup( id );
513
514 if ( at.getSyntax().isHumanReadable() )
515 {
516 if ( value instanceof byte[] )
517 {
518 try
519 {
520 return new ClientStringValue( new String( ( byte[] ) value, "UTF-8" ) );
521 }
522 catch ( UnsupportedEncodingException uee )
523 {
524 String message = "The value stored in an Human Readable attribute as a byte[] should be convertible to a String";
525 LOG.error( message );
526 throw new NamingException( message );
527 }
528 }
529 }
530 else
531 {
532 if ( value instanceof String )
533 {
534 try
535 {
536 return new ClientBinaryValue( ( ( String ) value ).getBytes( "UTF-8" ) );
537 }
538 catch ( UnsupportedEncodingException uee )
539 {
540 String message = "The value stored in a non Human Readable attribute as a String should be convertible to a byte[]";
541 LOG.error( message );
542 throw new NamingException( message );
543 }
544 }
545 }
546
547 return null;
548 }
549
550
551
552
553
554
555
556 private void checkFilter( ExprNode filter ) throws Exception
557 {
558 if ( filter == null )
559 {
560 String message = "A filter should not be null";
561 LOG.error( message );
562 throw new NamingException( message );
563 }
564
565 if ( filter.isLeaf() )
566 {
567 if ( filter instanceof EqualityNode )
568 {
569 EqualityNode node = ( ( EqualityNode ) filter );
570 Object value = node.getValue();
571
572 Value<?> newValue = convert( node.getAttribute(), value );
573
574 if ( newValue != null )
575 {
576 node.setValue( newValue );
577 }
578 }
579 else if ( filter instanceof SubstringNode )
580 {
581 SubstringNode node = ( ( SubstringNode ) filter );
582
583 if ( !atRegistry.lookup( node.getAttribute() ).getSyntax().isHumanReadable() )
584 {
585 String message = "A Substring filter should be used only on Human Readable attributes";
586 LOG.error( message );
587 throw new NamingException( message );
588 }
589 }
590 else if ( filter instanceof PresenceNode )
591 {
592
593 }
594 else if ( filter instanceof GreaterEqNode )
595 {
596 GreaterEqNode node = ( ( GreaterEqNode ) filter );
597 Object value = node.getValue();
598
599 Value<?> newValue = convert( node.getAttribute(), value );
600
601 if ( newValue != null )
602 {
603 node.setValue( newValue );
604 }
605
606 }
607 else if ( filter instanceof LessEqNode )
608 {
609 LessEqNode node = ( ( LessEqNode ) filter );
610 Object value = node.getValue();
611
612 Value<?> newValue = convert( node.getAttribute(), value );
613
614 if ( newValue != null )
615 {
616 node.setValue( newValue );
617 }
618 }
619 else if ( filter instanceof ExtensibleNode )
620 {
621 ExtensibleNode node = ( ( ExtensibleNode ) filter );
622
623 if ( !atRegistry.lookup( node.getAttribute() ).getSyntax().isHumanReadable() )
624 {
625 String message = "A Extensible filter should be used only on Human Readable attributes";
626 LOG.error( message );
627 throw new NamingException( message );
628 }
629 }
630 else if ( filter instanceof ApproximateNode )
631 {
632 ApproximateNode node = ( ( ApproximateNode ) filter );
633 Object value = node.getValue();
634
635 Value<?> newValue = convert( node.getAttribute(), value );
636
637 if ( newValue != null )
638 {
639 node.setValue( newValue );
640 }
641 }
642 else if ( filter instanceof AssertionNode )
643 {
644
645 return;
646 }
647 else if ( filter instanceof ScopeNode )
648 {
649
650 return;
651 }
652 }
653 else
654 {
655
656 for ( ExprNode child : ( ( BranchNode ) filter ).getChildren() )
657 {
658 checkFilter( child );
659 }
660 }
661 }
662
663
664 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext )
665 throws Exception
666 {
667 LdapDN base = opContext.getDn();
668 SearchControls searchCtls = opContext.getSearchControls();
669 ExprNode filter = opContext.getFilter();
670
671
672
673
674 if ( searchCtls.getReturningAttributes() != null )
675 {
676 filterAttributesToReturn( searchCtls );
677 }
678
679
680 checkFilter( filter );
681
682 String baseNormForm = ( base.isNormalized() ? base.getNormName() : base.toNormName() );
683
684
685 if ( !subschemaSubentryDnNorm.equals( baseNormForm ) )
686 {
687 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
688
689 if ( searchCtls.getReturningAttributes() != null )
690 {
691 cursor.addEntryFilter( topFilter );
692 return cursor;
693 }
694
695 for ( EntryFilter ef : filters )
696 {
697 cursor.addEntryFilter( ef );
698 }
699
700 return cursor;
701 }
702
703
704
705
706 if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
707 {
708
709 if ( filter instanceof SimpleNode )
710 {
711
712
713 SimpleNode node = ( SimpleNode ) filter;
714 String objectClass;
715
716 if ( node.getValue() instanceof ClientStringValue )
717 {
718 objectClass = ( String ) node.getValue().get();
719 }
720 else
721 {
722 objectClass = node.getValue().get().toString();
723 }
724
725 String objectClassOid = null;
726
727 if ( registries.getObjectClassRegistry().hasObjectClass( objectClass ) )
728 {
729 objectClassOid = registries.getObjectClassRegistry().lookup( objectClass ).getOid();
730 }
731 else
732 {
733 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext );
734 }
735
736 String nodeOid = registries.getOidRegistry().getOid( node.getAttribute() );
737
738
739 if ( nodeOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID )
740 && ( objectClassOid.equals( SchemaConstants.TOP_OC_OID ) || objectClassOid
741 .equals( SchemaConstants.SUBSCHEMA_OC_OID ) ) && ( node instanceof EqualityNode ) )
742 {
743
744 ServerEntry serverEntry = schemaService.getSubschemaEntry( searchCtls.getReturningAttributes() );
745 serverEntry.setDn( base );
746 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( serverEntry ), opContext );
747 }
748 else
749 {
750 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext );
751 }
752 }
753 else if ( filter instanceof PresenceNode )
754 {
755 PresenceNode node = ( PresenceNode ) filter;
756
757
758 if ( node.getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
759 {
760
761 ServerEntry serverEntry = schemaService.getSubschemaEntry( searchCtls.getReturningAttributes() );
762 serverEntry.setDn( base );
763 EntryFilteringCursor cursor = new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>(
764 serverEntry ), opContext );
765 return cursor;
766 }
767 }
768 }
769
770
771 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext );
772 }
773
774
775
776
777
778 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext )
779 throws Exception
780 {
781 ClonedServerEntry result = nextInterceptor.lookup( opContext );
782
783 if ( result == null )
784 {
785 return null;
786 }
787
788 filterBinaryAttributes( result );
789 filterObjectClass( result );
790
791 return result;
792 }
793
794
795 private void getSuperiors( ObjectClass oc, Set<String> ocSeen, List<ObjectClass> result ) throws Exception
796 {
797 for ( ObjectClass parent : oc.getSuperClasses() )
798 {
799
800 if ( SchemaConstants.TOP_OC.equals( parent.getName() ) )
801 {
802 continue;
803 }
804
805 if ( !ocSeen.contains( parent.getOid() ) )
806 {
807 ocSeen.add( parent.getOid() );
808 result.add( parent );
809 }
810
811
812 getSuperiors( parent, ocSeen, result );
813 }
814 }
815
816
817
818
819
820
821
822
823
824
825
826 private boolean isRequired( String attrId, EntryAttribute objectClasses ) throws Exception
827 {
828 OidRegistry oidRegistry = registries.getOidRegistry();
829 ObjectClassRegistry registry = registries.getObjectClassRegistry();
830
831 if ( !oidRegistry.hasOid( attrId ) )
832 {
833 return false;
834 }
835
836 String attrOid = oidRegistry.getOid( attrId );
837
838 for ( Value<?> objectClass : objectClasses )
839 {
840 ObjectClass ocSpec = registry.lookup( ( String ) objectClass.get() );
841
842 for ( AttributeType must : ocSpec.getMustList() )
843 {
844 if ( must.getOid().equals( attrOid ) )
845 {
846 return true;
847 }
848 }
849 }
850
851 return false;
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865 private boolean isCompleteRemoval( ServerAttribute change, ServerEntry entry ) throws Exception
866 {
867
868 if ( change.size() == 0 )
869 {
870 return true;
871 }
872
873
874
875
876
877 ServerAttribute changedEntryAttr = ( ServerAttribute ) entry.get( change.getUpId() ).clone();
878
879 for ( Value<?> value : change )
880 {
881 changedEntryAttr.remove( value );
882 }
883
884 return changedEntryAttr.size() == 0;
885 }
886
887
888
889
890
891
892
893
894
895
896 private EntryAttribute getResultantObjectClasses( ModificationOperation modOp, EntryAttribute changes,
897 EntryAttribute existing ) throws Exception
898 {
899 if ( ( changes == null ) && ( existing == null ) )
900 {
901 return new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
902 }
903
904 if ( changes == null )
905 {
906 return existing;
907 }
908
909 if ( ( existing == null ) && ( modOp == ModificationOperation.ADD_ATTRIBUTE ) )
910 {
911 return changes;
912 }
913 else if ( existing == null )
914 {
915 return new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
916 }
917
918 switch ( modOp )
919 {
920 case ADD_ATTRIBUTE:
921 for ( Value<?> value : changes )
922 {
923 existing.add( value );
924 }
925
926 return existing;
927
928 case REPLACE_ATTRIBUTE:
929 return ( ServerAttribute ) changes.clone();
930
931 case REMOVE_ATTRIBUTE:
932 for ( Value<?> value : changes )
933 {
934 existing.remove( value );
935 }
936
937 return existing;
938
939 default:
940 throw new InternalError( "" );
941 }
942 }
943
944
945 private boolean getObjectClasses( EntryAttribute objectClasses, List<ObjectClass> result ) throws Exception
946 {
947 Set<String> ocSeen = new HashSet<String>();
948 ObjectClassRegistry registry = registries.getObjectClassRegistry();
949
950
951
952 boolean hasExtensibleObject = false;
953
954 for ( Value<?> objectClass : objectClasses )
955 {
956 String objectClassName = ( String ) objectClass.get();
957
958 if ( SchemaConstants.TOP_OC.equals( objectClassName ) )
959 {
960 continue;
961 }
962
963 if ( SchemaConstants.EXTENSIBLE_OBJECT_OC.equalsIgnoreCase( objectClassName ) )
964 {
965 hasExtensibleObject = true;
966 }
967
968 ObjectClass oc = registry.lookup( objectClassName );
969
970
971 if ( !ocSeen.contains( oc.getOid() ) )
972 {
973 ocSeen.add( oc.getOid() );
974 result.add( oc );
975 }
976
977
978 getSuperiors( oc, ocSeen, result );
979 }
980
981 return hasExtensibleObject;
982 }
983
984
985 private Set<String> getAllMust( EntryAttribute objectClasses ) throws Exception
986 {
987 Set<String> must = new HashSet<String>();
988
989
990 for ( Value<?> value : objectClasses )
991 {
992 String ocName = ( String ) value.get();
993 ObjectClass oc = registries.getObjectClassRegistry().lookup( ocName );
994
995 AttributeType[] types = oc.getMustList();
996
997
998 if ( ( types != null ) && ( types.length > 0 ) )
999 {
1000 for ( AttributeType type : types )
1001 {
1002 must.add( type.getOid() );
1003 }
1004 }
1005 }
1006
1007 return must;
1008 }
1009
1010
1011 private Set<String> getAllAllowed( EntryAttribute objectClasses, Set<String> must ) throws Exception
1012 {
1013 Set<String> allowed = new HashSet<String>( must );
1014
1015
1016 allowed.add( registries.getOidRegistry().getOid( SchemaConstants.OBJECT_CLASS_AT ) );
1017
1018
1019 for ( Value<?> objectClass : objectClasses )
1020 {
1021 String ocName = ( String ) objectClass.get();
1022 ObjectClass oc = registries.getObjectClassRegistry().lookup( ocName );
1023
1024 AttributeType[] types = oc.getMayList();
1025
1026
1027 if ( ( types != null ) && ( types.length > 0 ) )
1028 {
1029 for ( AttributeType type : types )
1030 {
1031 String oid = type.getOid();
1032
1033 allowed.add( oid );
1034 }
1035 }
1036 }
1037
1038 return allowed;
1039 }
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051 private void alterObjectClasses( EntryAttribute objectClassAttr ) throws Exception
1052 {
1053 Set<String> objectClasses = new HashSet<String>();
1054 Set<String> objectClassesUP = new HashSet<String>();
1055
1056
1057 objectClasses.add( SchemaConstants.TOP_OC );
1058 objectClassesUP.add( SchemaConstants.TOP_OC );
1059
1060
1061 for ( Value<?> ocValue : objectClassAttr )
1062 {
1063 String ocName = ( String ) ocValue.get();
1064
1065 if ( !ocName.equalsIgnoreCase( SchemaConstants.TOP_OC ) )
1066 {
1067 String ocLowerName = ocName.toLowerCase();
1068
1069 ObjectClass objectClass = registries.getObjectClassRegistry().lookup( ocLowerName );
1070
1071 if ( !objectClasses.contains( ocLowerName ) )
1072 {
1073 objectClasses.add( ocLowerName );
1074 objectClassesUP.add( ocName );
1075 }
1076
1077 List<ObjectClass> ocSuperiors = superiors.get( objectClass.getOid() );
1078
1079 if ( ocSuperiors != null )
1080 {
1081 for ( ObjectClass oc : ocSuperiors )
1082 {
1083 if ( !objectClasses.contains( oc.getName().toLowerCase() ) )
1084 {
1085 objectClasses.add( oc.getName() );
1086 objectClassesUP.add( oc.getName() );
1087 }
1088 }
1089 }
1090 }
1091 }
1092
1093
1094 objectClassAttr.clear();
1095
1096 for ( String attribute : objectClassesUP )
1097 {
1098 objectClassAttr.add( attribute );
1099 }
1100 }
1101
1102
1103 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws Exception
1104 {
1105 LdapDN oriChildName = opContext.getDn();
1106
1107 ClonedServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
1108
1109 if ( oriChildName.startsWith( schemaBaseDN ) )
1110 {
1111 schemaManager.move( opContext, entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
1112 }
1113
1114 next.moveAndRename( opContext );
1115 }
1116
1117
1118 public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception
1119 {
1120 LdapDN oriChildName = opContext.getDn();
1121
1122 ClonedServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
1123
1124 if ( oriChildName.startsWith( schemaBaseDN ) )
1125 {
1126 schemaManager.replace( opContext, entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
1127 }
1128
1129 next.move( opContext );
1130 }
1131
1132
1133 public void rename( NextInterceptor next, RenameOperationContext opContext ) throws Exception
1134 {
1135 LdapDN name = opContext.getDn();
1136 Rdn newRdn = opContext.getNewRdn();
1137 boolean deleteOldRn = opContext.getDelOldDn();
1138 ServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
1139
1140 if ( deleteOldRn )
1141 {
1142 ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
1143 Rdn oldRDN = name.getRdn();
1144
1145
1146
1147
1148 for ( AttributeTypeAndValue atav : oldRDN )
1149 {
1150 AttributeType type = atRegistry.lookup( atav.getUpType() );
1151 String value = ( String ) atav.getNormValue();
1152 tmpEntry.remove( type, value );
1153 }
1154 for ( AttributeTypeAndValue atav : newRdn )
1155 {
1156 AttributeType type = atRegistry.lookup( atav.getUpType() );
1157 String value = ( String ) atav.getNormValue();
1158 if ( !tmpEntry.contains( type, value ) )
1159 {
1160 tmpEntry.add( new DefaultServerAttribute( type, value ) );
1161 }
1162 }
1163
1164
1165 LdapDN newDn = ( LdapDN ) name.clone();
1166 newDn.remove( name.size() - 1 );
1167 newDn.add( newRdn );
1168
1169 tmpEntry.setDn( newDn );
1170 check( name, tmpEntry );
1171
1172
1173 for ( AttributeTypeAndValue atav : oldRDN )
1174 {
1175 AttributeType attributeType = atRegistry.lookup( atav.getUpType() );
1176
1177 if ( !attributeType.isCanUserModify() )
1178 {
1179 throw new NoPermissionException( "Cannot modify the attribute '" + atav.getUpType() + "'" );
1180 }
1181 }
1182 }
1183
1184 if ( name.startsWith( schemaBaseDN ) )
1185 {
1186 schemaManager.modifyRn( opContext, entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
1187 }
1188
1189 next.rename( opContext );
1190 }
1191
1192
1193 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
1194 {
1195 ServerEntry entry;
1196 LdapDN name = opContext.getDn();
1197 List<Modification> mods = opContext.getModItems();
1198
1199
1200
1201 if ( name.getNormName().equalsIgnoreCase( subschemaSubentryDnNorm ) )
1202 {
1203 entry = schemaService.getSubschemaEntry( SCHEMA_SUBENTRY_RETURN_ATTRIBUTES );
1204 entry.setDn( name );
1205 }
1206 else
1207 {
1208 entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
1209 }
1210
1211
1212 ServerEntry targetEntry = (ServerEntry)SchemaUtils.getTargetEntry( mods , entry );
1213
1214 if ( entry == null )
1215 {
1216 LOG.error( "No entry with this name :{}", name );
1217 throw new LdapNameNotFoundException( "The entry which name is " + name + " is not found." );
1218 }
1219
1220
1221
1222 ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
1223
1224 Set<String> modset = new HashSet<String>();
1225 Modification objectClassMod = null;
1226
1227
1228
1229
1230
1231
1232 for ( Modification mod : mods )
1233 {
1234 if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
1235 {
1236 objectClassMod = mod;
1237 }
1238
1239
1240 if ( mod.getAttribute().size() == 0 )
1241 {
1242
1243 if ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE )
1244 {
1245 throw new LdapInvalidAttributeValueException( "No value is not a valid value for an attribute.",
1246 ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
1247 }
1248 }
1249
1250 StringBuffer keybuf = new StringBuffer();
1251 keybuf.append( mod.getOperation() );
1252 keybuf.append( mod.getAttribute().getId() );
1253
1254 for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
1255 {
1256 keybuf.append( value.get() );
1257 }
1258
1259 if ( !modset.add( keybuf.toString() ) && ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE ) )
1260 {
1261 throw new LdapAttributeInUseException( "found two copies of the following modification item: " + mod );
1262 }
1263 }
1264
1265
1266 EntryAttribute objectClass;
1267
1268 if ( objectClassMod == null )
1269 {
1270 objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1271
1272 if ( objectClass == null )
1273 {
1274 objectClass = new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
1275 }
1276 }
1277 else
1278 {
1279 objectClass = getResultantObjectClasses( objectClassMod.getOperation(), objectClassMod.getAttribute(),
1280 tmpEntry.get( SchemaConstants.OBJECT_CLASS_AT ) );
1281 }
1282
1283 ObjectClassRegistry ocRegistry = this.registries.getObjectClassRegistry();
1284
1285
1286
1287 for ( Modification mod : mods )
1288 {
1289 ModificationOperation modOp = mod.getOperation();
1290 ServerAttribute change = ( ServerAttribute ) mod.getAttribute();
1291
1292
1293 if ( ( change.getAttributeType() == null ) && !atRegistry.hasAttributeType( change.getUpId() )
1294 && !objectClass.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
1295 {
1296 throw new LdapInvalidAttributeIdentifierException();
1297 }
1298
1299
1300
1301 AttributeType attributeType = change.getAttributeType();
1302
1303 if ( attributeType == null )
1304 {
1305 attributeType = atRegistry.lookup( change.getUpId() );
1306 }
1307
1308 if ( !attributeType.isCanUserModify() )
1309 {
1310 throw new NoPermissionException( "Cannot modify the attribute '" + change.getUpId() + "'" );
1311 }
1312
1313 switch ( modOp )
1314 {
1315 case ADD_ATTRIBUTE:
1316 EntryAttribute attr = tmpEntry.get( attributeType.getName() );
1317
1318 if ( attr != null )
1319 {
1320 for ( Value<?> value : change )
1321 {
1322 attr.add( value );
1323 }
1324 }
1325 else
1326 {
1327 attr = new DefaultServerAttribute( change.getUpId(), attributeType );
1328
1329 for ( Value<?> value : change )
1330 {
1331 attr.add( value );
1332 }
1333
1334 tmpEntry.put( attr );
1335 }
1336
1337 break;
1338
1339 case REMOVE_ATTRIBUTE:
1340 if ( tmpEntry.get( change.getId() ) == null )
1341 {
1342 LOG.error( "Trying to remove an non-existant attribute: " + change.getUpId() );
1343 throw new LdapNoSuchAttributeException();
1344 }
1345
1346
1347 if ( change.size() == 0 )
1348 {
1349
1350
1351 if ( isRequired( change.getUpId(), objectClass ) )
1352 {
1353 LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
1354 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1355 }
1356 }
1357 else
1358 {
1359
1360
1361 if ( isRequired( change.getUpId(), objectClass ) && isCompleteRemoval( change, entry ) )
1362 {
1363 LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
1364 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1365 }
1366
1367
1368 EntryAttribute modified = tmpEntry.removeAttributes( change.getId() ).get( 0 );
1369
1370
1371 for ( Value<?> value : change )
1372 {
1373 modified.remove( value );
1374 }
1375
1376
1377
1378 if ( ( modified.size() == 0 ) && isRequired( change.getId(), objectClass ) )
1379 {
1380 LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
1381 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1382 }
1383
1384
1385 if ( modified.size() > 0 )
1386 {
1387 tmpEntry.put( modified );
1388 }
1389 }
1390
1391 SchemaChecker
1392 .preventRdnChangeOnModifyRemove( name, modOp, change, this.registries.getOidRegistry() );
1393 SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, change,
1394 objectClass );
1395 break;
1396
1397 case REPLACE_ATTRIBUTE:
1398 SchemaChecker.preventRdnChangeOnModifyReplace( name, modOp, change, registries.getOidRegistry() );
1399 SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, modOp, change );
1400
1401 attr = tmpEntry.get( change.getUpId() );
1402
1403 if ( attr != null )
1404 {
1405 tmpEntry.removeAttributes( change.getUpId() );
1406 }
1407
1408 attr = new DefaultServerAttribute( change.getUpId(), attributeType );
1409
1410 if ( change.size() != 0 )
1411 {
1412 for ( Value<?> value : change )
1413 {
1414 attr.add( value );
1415 }
1416
1417 tmpEntry.put( attr );
1418 }
1419
1420 break;
1421 }
1422 }
1423
1424 check( name, tmpEntry );
1425
1426
1427
1428 if ( objectClassMod != null )
1429 {
1430 ServerAttribute alteredObjectClass = ( ServerAttribute ) objectClass.clone();
1431 alterObjectClasses( alteredObjectClass );
1432
1433 if ( !alteredObjectClass.equals( objectClass ) )
1434 {
1435 ServerAttribute ocMods = ( ServerAttribute ) objectClassMod.getAttribute();
1436
1437 switch ( objectClassMod.getOperation() )
1438 {
1439 case ADD_ATTRIBUTE:
1440 if ( ocMods.contains( SchemaConstants.TOP_OC ) )
1441 {
1442 ocMods.remove( SchemaConstants.TOP_OC );
1443 }
1444
1445 for ( Value<?> value : alteredObjectClass )
1446 {
1447 if ( !objectClass.contains( value ) )
1448 {
1449 ocMods.add( value );
1450 }
1451 }
1452
1453 break;
1454
1455 case REMOVE_ATTRIBUTE:
1456 for ( Value<?> value : alteredObjectClass )
1457 {
1458 if ( !objectClass.contains( value ) )
1459 {
1460 ocMods.remove( value );
1461 }
1462 }
1463
1464 break;
1465
1466 case REPLACE_ATTRIBUTE:
1467 for ( Value<?> value : alteredObjectClass )
1468 {
1469 if ( !objectClass.contains( value ) )
1470 {
1471 ocMods.add( value );
1472 }
1473 }
1474
1475 break;
1476
1477 default:
1478 }
1479 }
1480 }
1481
1482 if ( name.startsWith( schemaBaseDN ) )
1483 {
1484 LOG.debug( "Modification attempt on schema partition {}: \n{}", name, opContext );
1485
1486 schemaManager.modify( opContext, entry, targetEntry, opContext
1487 .hasRequestControl( CascadeControl.CONTROL_OID ) );
1488 }
1489 else if ( subschemaSubentryDnNorm.equals( name.getNormName() ) )
1490 {
1491 LOG.debug( "Modification attempt on schema subentry {}: \n{}", name, opContext );
1492
1493 schemaManager.modifySchemaSubentry( opContext, entry, targetEntry, opContext
1494 .hasRequestControl( CascadeControl.CONTROL_OID ) );
1495 return;
1496 }
1497
1498 next.modify( opContext );
1499 }
1500
1501
1502
1503
1504
1505 private void filterAttributeTypes( SearchingOperationContext operation, ClonedServerEntry result )
1506 {
1507 if ( operation.getReturningAttributes() == null )
1508 {
1509 return;
1510 }
1511
1512 for ( AttributeTypeOptions attrOptions : operation.getReturningAttributes() )
1513 {
1514 EntryAttribute attribute = result.get( attrOptions.getAttributeType() );
1515
1516 if ( attrOptions.hasOption() )
1517 {
1518 for ( String option : attrOptions.getOptions() )
1519 {
1520 if ( "binary".equalsIgnoreCase( option ) )
1521 {
1522 continue;
1523 }
1524 else
1525 {
1526 try
1527 {
1528 if ( result.contains( attribute ) )
1529 {
1530 result.remove( attribute );
1531 }
1532 }
1533 catch ( NamingException ne )
1534 {
1535
1536 }
1537 break;
1538 }
1539 }
1540
1541 }
1542 }
1543 }
1544
1545
1546 private void filterObjectClass( ServerEntry entry ) throws Exception
1547 {
1548 List<ObjectClass> objectClasses = new ArrayList<ObjectClass>();
1549 EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1550
1551 if ( oc != null )
1552 {
1553 getObjectClasses( oc, objectClasses );
1554
1555 entry.removeAttributes( SchemaConstants.OBJECT_CLASS_AT );
1556
1557 ServerAttribute newOc = new DefaultServerAttribute( ( ( ServerAttribute ) oc ).getAttributeType() );
1558
1559 for ( ObjectClass currentOC : objectClasses )
1560 {
1561 newOc.add( currentOC.getName() );
1562 }
1563
1564 newOc.add( SchemaConstants.TOP_OC );
1565 entry.put( newOc );
1566 }
1567 }
1568
1569
1570 private void filterBinaryAttributes( ServerEntry entry ) throws Exception
1571 {
1572
1573
1574
1575
1576 for ( EntryAttribute attribute : entry )
1577 {
1578 if ( !( ( ServerAttribute ) attribute ).getAttributeType().getSyntax().isHumanReadable() )
1579 {
1580 List<Value<?>> binaries = new ArrayList<Value<?>>();
1581
1582 for ( Value<?> value : attribute )
1583 {
1584 Object attrValue = value.get();
1585
1586 if ( attrValue instanceof String )
1587 {
1588 binaries.add( new ServerBinaryValue( ( ( ServerAttribute ) attribute ).getAttributeType(),
1589 StringTools.getBytesUtf8( ( String ) attrValue ) ) );
1590 }
1591 else
1592 {
1593 binaries.add( new ServerBinaryValue( ( ( ServerAttribute ) attribute ).getAttributeType(),
1594 ( byte[] ) attrValue ) );
1595 }
1596 }
1597
1598 attribute.clear();
1599 attribute.put( binaries );
1600 }
1601 }
1602 }
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612 private class BinaryAttributeFilter implements EntryFilter
1613 {
1614 public boolean accept( SearchingOperationContext operation, ClonedServerEntry result ) throws Exception
1615 {
1616 filterBinaryAttributes( result );
1617 return true;
1618 }
1619 }
1620
1621
1622
1623
1624 private class TopFilter implements EntryFilter
1625 {
1626 public boolean accept( SearchingOperationContext operation, ClonedServerEntry result ) throws Exception
1627 {
1628 filterObjectClass( result );
1629 filterAttributeTypes( operation, result );
1630 return true;
1631 }
1632 }
1633
1634
1635
1636
1637
1638
1639
1640 private void check( LdapDN dn, ServerEntry entry ) throws Exception
1641 {
1642
1643
1644
1645
1646 for ( AttributeType attributeType : entry.getAttributeTypes() )
1647 {
1648 if ( !atRegistry.hasAttributeType( attributeType.getName() ) )
1649 {
1650 throw new LdapInvalidAttributeIdentifierException( attributeType.getName()
1651 + " not found in attribute registry!" );
1652 }
1653 }
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663 EntryAttribute objectClassAttr = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1664
1665
1666
1667
1668 if ( objectClassAttr == null )
1669 {
1670 objectClassAttr = new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
1671 }
1672
1673 List<ObjectClass> ocs = new ArrayList<ObjectClass>();
1674
1675 alterObjectClasses( objectClassAttr );
1676
1677
1678 Set<String> must = getAllMust( objectClassAttr );
1679 Set<String> allowed = getAllAllowed( objectClassAttr, must );
1680
1681 boolean hasExtensibleObject = getObjectClasses( objectClassAttr, ocs );
1682
1683
1684
1685 assertObjectClasses( dn, ocs );
1686
1687 assertRequiredAttributesPresent( dn, entry, must );
1688 assertNumberOfAttributeValuesValid( entry );
1689
1690 if ( !hasExtensibleObject )
1691 {
1692 assertAllAttributesAllowed( dn, entry, allowed );
1693 }
1694
1695
1696 assertHumanReadable( entry );
1697
1698
1699 assertSyntaxes( entry );
1700
1701
1702 Rdn rdn = entry.getDn().getRdn();
1703
1704
1705 for ( AttributeTypeAndValue ava : rdn )
1706 {
1707 String value = ( String ) ava.getNormValue();
1708 String upValue = ( String ) ava.getUpValue();
1709 String upId = ava.getUpType();
1710
1711
1712 if ( !entry.contains( upId, value ) )
1713 {
1714 String message = "The RDN '" + upId + "=" + upValue + "' is not present in the entry";
1715 LOG.warn( message );
1716
1717
1718
1719
1720 if ( !entry.containsAttribute( upId ) )
1721 {
1722 entry.add( upId, upValue );
1723 }
1724
1725 else
1726 {
1727 AttributeType at = atRegistry.lookup( upId );
1728
1729
1730 if ( at.isSingleValue() )
1731 {
1732 entry.removeAttributes( upId );
1733 entry.put( upId, upValue );
1734 }
1735
1736 else
1737 {
1738 entry.add( upId, upValue );
1739 }
1740 }
1741 }
1742 }
1743 }
1744
1745
1746 private void checkOcSuperior( ServerEntry entry ) throws Exception
1747 {
1748 ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
1749
1750
1751 EntryAttribute supOC = entry.get( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT );
1752
1753 if ( supOC != null )
1754 {
1755 ObjectClassTypeEnum ocType = ObjectClassTypeEnum.STRUCTURAL;
1756
1757 if ( entry.get( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT ) != null )
1758 {
1759 String type = entry.get( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT ).getString();
1760 ocType = ObjectClassTypeEnum.getClassType( type );
1761 }
1762
1763
1764
1765 for ( Value<?> sup:supOC )
1766 {
1767 try
1768 {
1769 String supName = (String)sup.get();
1770
1771 ObjectClass superior = ocRegistry.lookup( supName );
1772
1773 switch ( ocType )
1774 {
1775 case ABSTRACT :
1776 if ( !superior.isAbstract() )
1777 {
1778 String message = "An ABSTRACT ObjectClass cannot inherit from an objectClass which is not ABSTRACT";
1779 LOG.error( message );
1780 throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1781 }
1782
1783 break;
1784
1785 case AUXILIARY :
1786 if ( !superior.isAbstract() && ! superior.isAuxiliary() )
1787 {
1788 String message = "An AUXILiARY ObjectClass cannot inherit from an objectClass which is not ABSTRACT or AUXILIARY";
1789 LOG.error( message );
1790 throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1791 }
1792
1793 break;
1794
1795 case STRUCTURAL :
1796 break;
1797 }
1798 }
1799 catch ( NamingException ne )
1800 {
1801
1802 String message = "Cannot have a superior which does not exist";
1803 LOG.error( message );
1804 throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1805 }
1806 }
1807 }
1808 }
1809
1810
1811
1812
1813
1814 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception
1815 {
1816 LdapDN name = addContext.getDn();
1817 ServerEntry entry = addContext.getEntry();
1818
1819 check( name, entry );
1820
1821
1822 if ( name.startsWith( schemaBaseDN ) )
1823 {
1824 schemaManager.add( addContext );
1825
1826 checkOcSuperior( addContext.getEntry() );
1827 }
1828
1829 next.add( addContext );
1830 }
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840 private void assertAllAttributesAllowed( LdapDN dn, ServerEntry entry, Set<String> allowed ) throws Exception
1841 {
1842
1843
1844 EntryAttribute objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1845
1846 if ( objectClass.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
1847 {
1848 return;
1849 }
1850
1851 for ( EntryAttribute attribute : entry )
1852 {
1853 String attrOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
1854
1855 AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
1856
1857 if ( !attributeType.isCollective() && ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) )
1858 {
1859 if ( !allowed.contains( attrOid ) )
1860 {
1861 throw new LdapSchemaViolationException( "Attribute " + attribute.getUpId()
1862 + " not declared in objectClasses of entry " + dn.getUpName(),
1863 ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1864 }
1865 }
1866 }
1867 }
1868
1869
1870 public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception
1871 {
1872 LdapDN name = opContext.getDn();
1873
1874 if ( name.startsWith( schemaBaseDN ) )
1875 {
1876 ClonedServerEntry entry = nexus.lookup( opContext.newLookupContext( name ) );
1877 schemaManager.delete( opContext, entry, opContext.hasRequestControl( CascadeControl.CONTROL_OID ) );
1878 }
1879
1880 next.delete( opContext );
1881 }
1882
1883
1884
1885
1886
1887 private void assertNumberOfAttributeValuesValid( Entry entry ) throws InvalidAttributeValueException, Exception
1888 {
1889 for ( EntryAttribute attribute : entry )
1890 {
1891 assertNumberOfAttributeValuesValid( attribute );
1892 }
1893 }
1894
1895
1896
1897
1898
1899 private void assertNumberOfAttributeValuesValid( EntryAttribute attribute ) throws InvalidAttributeValueException,
1900 Exception
1901 {
1902 if ( attribute.size() > 1 && ( ( ServerAttribute ) attribute ).getAttributeType().isSingleValue() )
1903 {
1904 throw new LdapInvalidAttributeValueException( "More than one value has been provided "
1905 + "for the single-valued attribute: " + attribute.getUpId(), ResultCodeEnum.CONSTRAINT_VIOLATION );
1906 }
1907 }
1908
1909
1910
1911
1912
1913 private void assertRequiredAttributesPresent( LdapDN dn, Entry entry, Set<String> must ) throws Exception
1914 {
1915 for ( EntryAttribute attribute : entry )
1916 {
1917 must.remove( ( ( ServerAttribute ) attribute ).getAttributeType().getOid() );
1918 }
1919
1920 if ( must.size() != 0 )
1921 {
1922 throw new LdapSchemaViolationException( "Required attributes " + must + " not found within entry "
1923 + dn.getUpName(), ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1924 }
1925 }
1926
1927
1928
1929
1930
1931
1932
1933
1934 private void assertObjectClasses( LdapDN dn, List<ObjectClass> ocs ) throws Exception
1935 {
1936 Set<ObjectClass> structuralObjectClasses = new HashSet<ObjectClass>();
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949 for ( ObjectClass oc : ocs )
1950 {
1951 if ( oc.isStructural() )
1952 {
1953 structuralObjectClasses.add( oc );
1954 }
1955 }
1956
1957
1958
1959
1960
1961 if ( structuralObjectClasses.isEmpty() )
1962 {
1963 String message = "Entry " + dn + " does not contain a STRUCTURAL ObjectClass";
1964 LOG.error( message );
1965 throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1966 }
1967
1968
1969
1970
1971
1972
1973
1974
1975 Set<ObjectClass> remaining = new HashSet<ObjectClass>( structuralObjectClasses.size() );
1976 remaining.addAll( structuralObjectClasses );
1977 for ( ObjectClass oc : structuralObjectClasses )
1978 {
1979 if ( oc.getSuperClasses() != null )
1980 {
1981 for ( ObjectClass superClass : oc.getSuperClasses() )
1982 {
1983 if ( superClass.isStructural() )
1984 {
1985 remaining.remove( superClass );
1986 }
1987 }
1988 }
1989 }
1990
1991
1992 if ( remaining.size() > 1 )
1993 {
1994 String message = "Entry " + dn + " contains more than one STRUCTURAL ObjectClass: " + remaining;
1995 LOG.error( message );
1996 throw new LdapSchemaViolationException( message, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
1997 }
1998 }
1999
2000
2001
2002
2003
2004 private void assertSyntaxes( Entry entry ) throws Exception
2005 {
2006
2007 for ( EntryAttribute attribute : entry )
2008 {
2009 AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
2010 SyntaxChecker syntaxChecker = attributeType.getSyntax().getSyntaxChecker();
2011
2012 if ( syntaxChecker instanceof AcceptAllSyntaxChecker )
2013 {
2014
2015
2016 continue;
2017 }
2018
2019
2020 for ( Value<?> value : attribute )
2021 {
2022 try
2023 {
2024 syntaxChecker.assertSyntax( value.get() );
2025 }
2026 catch ( Exception ne )
2027 {
2028 String message = "Attribute value '"
2029 + ( value instanceof ServerStringValue ? value.get() : StringTools.dumpBytes( ( byte[] ) value
2030 .get() ) ) + "' for attribute '" + attribute.getUpId() + "' is syntactically incorrect";
2031 LOG.info( message );
2032
2033 throw new LdapInvalidAttributeValueException( message, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
2034 }
2035 }
2036 }
2037 }
2038
2039
2040
2041
2042
2043
2044
2045 private boolean checkHumanReadable( EntryAttribute attribute ) throws Exception
2046 {
2047 boolean isModified = false;
2048
2049
2050 for ( Value<?> value : attribute )
2051 {
2052 if ( value instanceof ServerStringValue )
2053 {
2054 continue;
2055 }
2056 else if ( value instanceof ServerBinaryValue )
2057 {
2058
2059
2060 try
2061 {
2062 String valStr = new String( ( byte[] ) value.get(), "UTF-8" );
2063 attribute.remove( value );
2064 attribute.add( valStr );
2065 isModified = true;
2066 }
2067 catch ( UnsupportedEncodingException uee )
2068 {
2069 throw new NamingException( "The value is not a valid String" );
2070 }
2071 }
2072 else
2073 {
2074 throw new NamingException( "The value stored in an Human Readable attribute is not a String" );
2075 }
2076 }
2077
2078 return isModified;
2079 }
2080
2081
2082
2083
2084
2085
2086
2087 private boolean checkNotHumanReadable( EntryAttribute attribute ) throws Exception
2088 {
2089 boolean isModified = false;
2090
2091
2092 for ( Value<?> value : attribute )
2093 {
2094 if ( value instanceof ServerBinaryValue )
2095 {
2096 continue;
2097 }
2098 else if ( value instanceof ServerStringValue )
2099 {
2100
2101
2102 try
2103 {
2104 byte[] valBytes = ( ( String ) value.get() ).getBytes( "UTF-8" );
2105
2106 attribute.remove( value );
2107 attribute.add( valBytes );
2108 isModified = true;
2109 }
2110 catch ( UnsupportedEncodingException uee )
2111 {
2112 String message = "The value stored in a not Human Readable attribute as a String should be convertible to a byte[]";
2113 LOG.error( message );
2114 throw new NamingException( message );
2115 }
2116 }
2117 else
2118 {
2119 String message = "The value is not valid. It should be a String or a byte[]";
2120 LOG.error( message );
2121 throw new NamingException( message );
2122 }
2123 }
2124
2125 return isModified;
2126 }
2127
2128
2129
2130
2131
2132
2133
2134 private void assertHumanReadable( ServerEntry entry ) throws Exception
2135 {
2136 boolean isModified = false;
2137
2138 ServerEntry clonedEntry = null;
2139
2140
2141 for ( EntryAttribute attribute : entry )
2142 {
2143 AttributeType attributeType = ( ( ServerAttribute ) attribute ).getAttributeType();
2144
2145
2146 if ( attributeType.getSyntax().isHumanReadable() )
2147 {
2148 isModified = checkHumanReadable( attribute );
2149 }
2150 else
2151 {
2152 isModified = checkNotHumanReadable( attribute );
2153 }
2154
2155
2156
2157 if ( isModified )
2158 {
2159 if ( clonedEntry == null )
2160 {
2161 clonedEntry = ( ServerEntry ) entry.clone();
2162 }
2163
2164
2165 clonedEntry.put( attribute );
2166
2167 isModified = false;
2168 }
2169 }
2170
2171 if ( clonedEntry != null )
2172 {
2173 entry = clonedEntry;
2174 }
2175 }
2176 }