View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.core.schema;
21  
22  
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
30  import org.apache.directory.api.ldap.model.entry.Attribute;
31  import org.apache.directory.api.ldap.model.entry.Modification;
32  import org.apache.directory.api.ldap.model.exception.LdapException;
33  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
34  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
35  import org.apache.directory.api.ldap.model.schema.AttributeType;
36  import org.apache.directory.api.ldap.model.schema.DitContentRule;
37  import org.apache.directory.api.ldap.model.schema.DitStructureRule;
38  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
39  import org.apache.directory.api.ldap.model.schema.MatchingRule;
40  import org.apache.directory.api.ldap.model.schema.MatchingRuleUse;
41  import org.apache.directory.api.ldap.model.schema.NameForm;
42  import org.apache.directory.api.ldap.model.schema.ObjectClass;
43  import org.apache.directory.api.ldap.model.schema.SchemaManager;
44  import org.apache.directory.api.ldap.model.schema.parsers.LdapComparatorDescription;
45  import org.apache.directory.api.ldap.model.schema.parsers.NormalizerDescription;
46  import org.apache.directory.api.ldap.model.schema.parsers.SyntaxCheckerDescription;
47  import org.apache.directory.api.util.Strings;
48  import org.apache.directory.server.core.api.DirectoryService;
49  import org.apache.directory.server.core.api.DnFactory;
50  import org.apache.directory.server.core.api.InterceptorEnum;
51  import org.apache.directory.server.core.api.OperationEnum;
52  import org.apache.directory.server.core.api.interceptor.Interceptor;
53  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
54  import org.apache.directory.server.core.api.schema.DescriptionParsers;
55  import org.apache.directory.server.i18n.I18n;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  
60  /**
61   *
62   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
63   */
64  public class SchemaSubentryManager
65  {
66      /** A logger for this class */
67      private static final Logger LOG = LoggerFactory.getLogger( SchemaSubentryManager.class );
68  
69      // indices of handlers and object ids into arrays
70      private static final int COMPARATOR_INDEX = 0;
71      private static final int NORMALIZER_INDEX = 1;
72      private static final int SYNTAX_CHECKER_INDEX = 2;
73      private static final int SYNTAX_INDEX = 3;
74      private static final int MATCHING_RULE_INDEX = 4;
75      private static final int ATTRIBUTE_TYPE_INDEX = 5;
76      private static final int OBJECT_CLASS_INDEX = 6;
77      private static final int MATCHING_RULE_USE_INDEX = 7;
78      private static final int DIT_STRUCTURE_RULE_INDEX = 8;
79      private static final int DIT_CONTENT_RULE_INDEX = 9;
80      private static final int NAME_FORM_INDEX = 10;
81  
82      private static final Set<String> VALID_OU_VALUES = new HashSet<>();
83  
84      /** The schemaManager */
85      private final SchemaManager schemaManager;
86  
87      private final SchemaSubentryModifier subentryModifier;
88  
89      /** The description parsers */
90      private final DescriptionParsers parsers;
91  
92      /**
93       * Maps the OID of a subschemaSubentry operational attribute to the index of
94       * the handler in the schemaObjectHandlers array.
95       */
96      private final Map<String, Integer> opAttr2handlerIndex = new HashMap<>( 11 );
97      private static final String CASCADING_ERROR =
98          "Cascading has not yet been implemented: standard operation is in effect.";
99  
100     static
101     {
102         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.NORMALIZERS_AT ) );
103         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.COMPARATORS_AT ) );
104         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.SYNTAX_CHECKERS_AT ) );
105         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.SYNTAXES ) );
106         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.MATCHING_RULES_AT ) );
107         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.MATCHING_RULE_USE_AT ) );
108         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.ATTRIBUTE_TYPES_AT ) );
109         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.OBJECT_CLASSES_AT ) );
110         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.NAME_FORMS_AT ) );
111         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.DIT_CONTENT_RULES_AT ) );
112         VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );
113     }
114 
115 
116     public SchemaSubentryManager( SchemaManager schemaManager, DnFactory dnFactory )
117         throws LdapException
118     {
119         this.schemaManager = schemaManager;
120         this.subentryModifier = new SchemaSubentryModifier( schemaManager, dnFactory );
121         this.parsers = new DescriptionParsers( schemaManager );
122 
123         String comparatorsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.COMPARATORS_AT );
124         opAttr2handlerIndex.put( comparatorsOid, COMPARATOR_INDEX );
125 
126         String normalizersOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NORMALIZERS_AT );
127         opAttr2handlerIndex.put( normalizersOid, NORMALIZER_INDEX );
128 
129         String syntaxCheckersOid = schemaManager.getAttributeTypeRegistry().getOidByName(
130             SchemaConstants.SYNTAX_CHECKERS_AT );
131         opAttr2handlerIndex.put( syntaxCheckersOid, SYNTAX_CHECKER_INDEX );
132 
133         String ldapSyntaxesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
134             SchemaConstants.LDAP_SYNTAXES_AT );
135         opAttr2handlerIndex.put( ldapSyntaxesOid, SYNTAX_INDEX );
136 
137         String matchingRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
138             SchemaConstants.MATCHING_RULES_AT );
139         opAttr2handlerIndex.put( matchingRulesOid, MATCHING_RULE_INDEX );
140 
141         String attributeTypesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
142             SchemaConstants.ATTRIBUTE_TYPES_AT );
143         opAttr2handlerIndex.put( attributeTypesOid, ATTRIBUTE_TYPE_INDEX );
144 
145         String objectClassesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
146             SchemaConstants.OBJECT_CLASSES_AT );
147         opAttr2handlerIndex.put( objectClassesOid, OBJECT_CLASS_INDEX );
148 
149         String matchingRuleUseOid = schemaManager.getAttributeTypeRegistry().getOidByName(
150             SchemaConstants.MATCHING_RULE_USE_AT );
151         opAttr2handlerIndex.put( matchingRuleUseOid, MATCHING_RULE_USE_INDEX );
152 
153         String ditStructureRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
154             SchemaConstants.DIT_STRUCTURE_RULES_AT );
155         opAttr2handlerIndex.put( ditStructureRulesOid, DIT_STRUCTURE_RULE_INDEX );
156 
157         String ditContentRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
158             SchemaConstants.DIT_CONTENT_RULES_AT );
159         opAttr2handlerIndex.put( ditContentRulesOid, DIT_CONTENT_RULE_INDEX );
160 
161         String nameFormsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NAME_FORMS_AT );
162         opAttr2handlerIndex.put( nameFormsOid, NAME_FORM_INDEX );
163     }
164 
165 
166     /**
167      * Find the next interceptor in an operation's list of interceptors, assuming that
168      * we are already processing an operation, and we have stopped in a specific
169      * interceptor.<br>
170      * For instance, if the list of all the interceptors is : <br>
171      * [A, B, C, D, E, F]<br>
172      * and we ave two operations op1 and op2 with the following interceptors list : <br>
173      * op1 -> [A, D, F]<br>
174      * op2 -> [B, C, E]<br>
175      * then assuming that we have stopped at D, then op1.next -> F and op2.next -> E.
176      */
177     private Interceptor findNextInterceptor( OperationEnum operation, DirectoryService directoryService )
178     {
179         Interceptor interceptor = null;
180 
181         List<Interceptor> allInterceptors = directoryService.getInterceptors();
182         List<String> operationInterceptors = directoryService.getInterceptors( operation );
183         int position = 0;
184         String addInterceptor = operationInterceptors.get( position );
185 
186         for ( Interceptor inter : allInterceptors )
187         {
188             String interName = inter.getName();
189 
190             if ( interName.equals( InterceptorEnum.SCHEMA_INTERCEPTOR.getName() ) )
191             {
192                 // Found, get out
193                 position++;
194 
195                 if ( position < operationInterceptors.size() )
196                 {
197                     interceptor = directoryService.getInterceptor( operationInterceptors.get( position ) );
198                 }
199 
200                 break;
201             }
202 
203             if ( interName.equals( addInterceptor ) )
204             {
205                 position++;
206                 addInterceptor = operationInterceptors.get( position );
207             }
208         }
209 
210         return interceptor;
211     }
212 
213 
214     /**
215      * Find the position in the operation's list knowing the inteceptor name.
216      */
217     private int findPosition( OperationEnum operation, Interceptor interceptor, DirectoryService directoryService )
218     {
219         int position = 1;
220 
221         List<String> interceptors = directoryService.getInterceptors( operation );
222 
223         String interceptorName = interceptor.getName();
224 
225         for ( String name : interceptors )
226         {
227             if ( name.equals( interceptorName ) )
228             {
229                 break;
230             }
231 
232             position++;
233         }
234 
235         return position;
236     }
237 
238 
239     /**
240      * Update the SubschemaSubentry with all the modifications
241      * 
242      * @param modifyContext The Modification context
243      * @param doCascadeModify If we should recursively apply the modification
244      * @throws LdapException If the schema modification failed
245      */
246     public void modifySchemaSubentry( ModifyOperationContext modifyContext, boolean doCascadeModify )
247         throws LdapException
248     {
249         DirectoryService directoryService = modifyContext.getSession().getDirectoryService();
250 
251         // Compute the next interceptor for the Add and Delete operation, starting from
252         // the schemaInterceptor. We also need to get the position of this next interceptor
253         // in the operation's list.
254         Interceptor nextAdd = findNextInterceptor( OperationEnum.ADD, directoryService );
255         int positionAdd = findPosition( OperationEnum.ADD, nextAdd, directoryService );
256         Interceptor nextDelete = findNextInterceptor( OperationEnum.DELETE, directoryService );
257         int positionDelete = findPosition( OperationEnum.DELETE, nextDelete, directoryService );
258 
259         for ( Modification mod : modifyContext.getModItems() )
260         {
261             String opAttrOid = schemaManager.getAttributeTypeRegistry().getOidByName( mod.getAttribute().getId() );
262 
263             Attribute serverAttribute = mod.getAttribute();
264 
265             switch ( mod.getOperation() )
266             {
267                 case ADD_ATTRIBUTE:
268                     modifyAddOperation( nextAdd, positionAdd, modifyContext, opAttrOid, serverAttribute,
269                         doCascadeModify );
270                     break;
271 
272                 case REMOVE_ATTRIBUTE:
273                     modifyRemoveOperation( nextDelete, positionDelete, modifyContext, opAttrOid, serverAttribute );
274                     break;
275 
276                 case REPLACE_ATTRIBUTE:
277                     // a hack to allow entryCSN modification
278                     if ( directoryService.getAtProvider().getEntryCSN().equals( serverAttribute.getAttributeType() ) )
279                     {
280                         break;
281                     }
282 
283                     throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
284                         I18n.err( I18n.ERR_283 ) );
285 
286                 default:
287                     throw new IllegalStateException( I18n.err( I18n.ERR_284, mod.getOperation() ) );
288             }
289         }
290     }
291 
292 
293     /**
294      * Handles the modify remove operation on the subschemaSubentry for schema entities.
295      * 
296      * @param opAttrOid the numeric id of the operational attribute modified
297      * @param mods the attribute with the modifications
298      * to effect all dependents on the changed entity
299      * @throws Exception if there are problems updating the registries and the
300      * schema partition
301      */
302     private void modifyRemoveOperation( Interceptor nextInterceptor, int position,
303         ModifyOperationContext modifyContext, String opAttrOid,
304         Attribute mods ) throws LdapException
305     {
306         int index = opAttr2handlerIndex.get( opAttrOid );
307 
308         switch ( index )
309         {
310             case COMPARATOR_INDEX :
311                 LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
312 
313                 for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
314                 {
315                     subentryModifier.delete( nextInterceptor, position, modifyContext, comparatorDescription );
316                 }
317 
318                 break;
319 
320             case NORMALIZER_INDEX :
321                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
322 
323                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
324                 {
325                     subentryModifier.delete( nextInterceptor, position, modifyContext, normalizerDescription );
326                 }
327 
328                 break;
329 
330             case SYNTAX_CHECKER_INDEX :
331                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
332 
333                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
334                 {
335                     subentryModifier.delete( nextInterceptor, position, modifyContext, syntaxCheckerDescription );
336                 }
337 
338                 break;
339 
340             case SYNTAX_INDEX :
341                 LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
342 
343                 for ( LdapSyntax syntax : syntaxes )
344                 {
345                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, syntax );
346                 }
347 
348                 break;
349 
350             case MATCHING_RULE_INDEX :
351                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
352 
353                 for ( MatchingRule mr : mrs )
354                 {
355                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, mr );
356                 }
357 
358                 break;
359 
360             case ATTRIBUTE_TYPE_INDEX :
361                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
362 
363                 for ( AttributeType at : ats )
364                 {
365                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, at );
366                 }
367 
368                 break;
369 
370             case OBJECT_CLASS_INDEX :
371                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
372 
373                 for ( ObjectClass oc : ocs )
374                 {
375                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, oc );
376                 }
377 
378                 break;
379 
380             case MATCHING_RULE_USE_INDEX :
381                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
382 
383                 for ( MatchingRuleUse mru : mrus )
384                 {
385                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, mru );
386                 }
387 
388                 break;
389 
390             case DIT_STRUCTURE_RULE_INDEX :
391                 DitStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
392 
393                 for ( DitStructureRule dsr : dsrs )
394                 {
395                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, dsr );
396                 }
397 
398                 break;
399 
400             case DIT_CONTENT_RULE_INDEX :
401                 DitContentRule[] dcrs = parsers.parseDitContentRules( mods );
402 
403                 for ( DitContentRule dcr : dcrs )
404                 {
405                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, dcr );
406                 }
407 
408                 break;
409 
410             case NAME_FORM_INDEX :
411                 NameForm[] nfs = parsers.parseNameForms( mods );
412 
413                 for ( NameForm nf : nfs )
414                 {
415                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, nf );
416                 }
417 
418                 break;
419 
420             default:
421                 throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
422         }
423     }
424 
425 
426     /**
427      * Handles the modify add operation on the subschemaSubentry for schema entities.
428      * 
429      * @param opAttrOid the numeric id of the operational attribute modified
430      * @param mods the attribute with the modifications
431      * @param doCascadeModify determines if a cascading operation should be performed
432      * to effect all dependents on the changed entity
433      * @throws Exception if there are problems updating the registries and the
434      * schema partition
435      */
436     private void modifyAddOperation( Interceptor nextInterceptor, int position, ModifyOperationContext modifyContext,
437         String opAttrOid,
438         Attribute mods, boolean doCascadeModify ) throws LdapException
439     {
440         if ( doCascadeModify )
441         {
442             LOG.error( CASCADING_ERROR );
443         }
444 
445         int index = opAttr2handlerIndex.get( opAttrOid );
446 
447         switch ( index )
448         {
449             case COMPARATOR_INDEX :
450                 LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
451 
452                 for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
453                 {
454                     subentryModifier.add( nextInterceptor, position, modifyContext, comparatorDescription );
455                 }
456 
457                 break;
458 
459             case NORMALIZER_INDEX :
460                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
461 
462                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
463                 {
464                     subentryModifier.add( nextInterceptor, position, modifyContext, normalizerDescription );
465                 }
466 
467                 break;
468 
469             case SYNTAX_CHECKER_INDEX :
470                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
471 
472                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
473                 {
474                     subentryModifier.add( nextInterceptor, position, modifyContext, syntaxCheckerDescription );
475                 }
476 
477                 break;
478 
479             case SYNTAX_INDEX :
480                 LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
481 
482                 for ( LdapSyntax syntax : syntaxes )
483                 {
484                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, syntax );
485                 }
486 
487                 break;
488 
489             case MATCHING_RULE_INDEX :
490                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
491 
492                 for ( MatchingRule mr : mrs )
493                 {
494                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, mr );
495                 }
496 
497                 break;
498 
499             case ATTRIBUTE_TYPE_INDEX :
500                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
501 
502                 for ( AttributeType at : ats )
503                 {
504                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, at );
505                 }
506 
507                 break;
508 
509             case OBJECT_CLASS_INDEX :
510                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
511 
512                 for ( ObjectClass oc : ocs )
513                 {
514                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, oc );
515                 }
516 
517                 break;
518 
519             case MATCHING_RULE_USE_INDEX :
520                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
521 
522                 for ( MatchingRuleUse mru : mrus )
523                 {
524                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, mru );
525                 }
526 
527                 break;
528 
529             case DIT_STRUCTURE_RULE_INDEX :
530                 DitStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
531 
532                 for ( DitStructureRule dsr : dsrs )
533                 {
534                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, dsr );
535                 }
536 
537                 break;
538 
539             case DIT_CONTENT_RULE_INDEX :
540                 DitContentRule[] dcrs = parsers.parseDitContentRules( mods );
541 
542                 for ( DitContentRule dcr : dcrs )
543                 {
544                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, dcr );
545                 }
546 
547                 break;
548 
549             case NAME_FORM_INDEX :
550                 NameForm[] nfs = parsers.parseNameForms( mods );
551 
552                 for ( NameForm nf : nfs )
553                 {
554                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, nf );
555                 }
556 
557                 break;
558 
559             default:
560                 throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
561         }
562     }
563 }