Introduction

This interceptor is used to check that the object being manipulated are correct with respect to the LDAP schema. Currently, we have only one schema for all the partitions, so we will just check the elements correctness against this global schema

Structure

The Class diagram for this interceptor is shown in this diagram.

The interface (Interceptor) describes all the method to be implemented. The abstract class BaseInterceptor only declare 2 new methods.

The SchemaService class implements all the needed methods,not all the interface's methods.

Operations

We won't have to implement every operations in the interceptor : some of them are not necessary, like operations which do not modify the entries. For instance, bind() operation is not implemented.

Here is the list of operations defined in the interface, and the list of operations we implement in SchemaService (the lacking methos are already implemented in the intermediate abstract class) :

Interface

SchemaService

add

(tick)

addContextPartition

(error)

bind

(error)

compare

(error)

delete

(error)

destroy

(error)

getMatchedName

(error)

getRootDSE

(error)

getSuffix

(error)

hasEntry

(error)

init

(tick)

Interface

SchemaService

isSuffix

(error)

list

(tick)

listSuffixes

(error)

lookup

(tick)

modify

(tick)

modifyRn

(error)

move

(error)

removeContextPartition

(error)

search

(tick)

unbind

(error)

add operation

The add operation is associated to the AddRequest operation. It is used to add an entry.

We have to check that all attributes are correct :

  1. each attribute must exist in the schema
  2. each MUST attribute is present
  3. operationnal attributes which are not user modifiable must not be present
  4. single valued attributes must not contains more than one value
  5. all attributes must be in entry's gathered ObjectClasses MAY and MUST attributes.
  6. in the special case of collective attributes, we must check that the associated subentry allow their use
  7. in the special case of H-R attributes, we must check that their values are String or at least, byte[] representing a String UTF-8 encoded. In this case, we will transform the byte[] to a String

All those controls are done in a single helper method called check(), described further on this page.

If any of these condition is not respected, then a NamingException is thrown.

The 3rd check is presently not implemented ...

init operation

This is not really an operation per se, but the initialization of the interceptor. We create the filters used by the search operation, and create the objectClasses superiors mapping.

There is a clear reason why we compute the ObjectClasses superiors here : we don't want to do it for each request. The idea is to gather, for each objectClass, its MAY, MUST and Allowed attributes. As an objectClass may have superiors, we must do it recursively.

Two methods are used for that : computeSuperiors() and computeOCSuperiors(). The first one iterate through all the declared objectclasses, the second one does the recursive analysis.

list operation

This operation is used internally to the server, it does not correspond to a Ldap Request.

The only thing we should do in this operation with respect to schema is to filter the binary attributes : all attribute values of an attributeType which is seen as non human readible will be transformed to byte[] if they were stored as String.

I don't really think this is a necessary operation. Such attribute values are already - or should have already been - transformed to byte[] when they were added or modified. In my mind, this method should be removed from the operation to be implemented.

lookup operation

This operation is used internally to the server, it does not correspond to a Ldap Request.

We have two forms of this operation :

  • the first one just look for an entry, given a DN
  • the second one look for an entry from its DN, and filters the attributes with respect to the given list.

However, this is a two steps process. We ask the next interceptor to return the entry, if any, and then we process the attributes to be sure we have all hat we need :

get the entry with DN
if no entry found,
  return null
filter binary attributes (cf list method's note)
filter ObjectClasses
return the modified entry

The important part is the ObjectClasses filtering : we may have stored an entry with missing objectClasses (for instance, as the person objectClass inherit from top objectClass, users often omit to add this top objectClass in the entry. This method is just here to add all missing objectClasses)

I think that this ObjectClasses filtering is useless : all entries are already completed when added or modified.
To be checked.

If we remove the binary attribute and objectClasses filterings, then we don't need anymore to implement this method in this interceptor.

modify operation

The modify operation is the most complicated operation we have to deal with. We must check that all the modified (added, modified or deleted) attributes are valid.

Three kinds of modification can occurs :

  • add
  • modify
  • delete

Modify and delete operation can be done on the entire attribute, or on a single value.

All the three operations have a different semantic, and should be considered one by one. However, the general way to deal with them is simple :

for each modification operation
  apply brutally the modification on the entry without checking anything
  if an exception occurs,
    gather this exception in a list of exceptions
when done, check that the modified entry is correct (the very same way we do it for an Add operation)
if the entry is not correct
  then throw a NamingExeption, with the list of gathered exceptions

Doing so will allow us to respect this rule :

"The entire list of modifications MUST be performed in the order they are listed as a single atomic operation. While individual modifications may violate certain aspects of the directory schema (such as the object class definition and Directory Information Tree (DIT) content rule), the resulting entry after the entire list of modifications is performed MUST conform to the requirements of the directory model and controlling schema" (RFC 2251/4511)

To be franck, this rule seems strange. This is questionnable that we should not throw any exception in the middle of an operation even if the final result is correct.

We have to check all the following points :

  1. The values must exists in the schema
  2. The attribute must exists into the entry
  3. The attribute must not have the NO_USER_MODIFICATION flag set
  4. If we have to delete the attribute (all the values are to be removed), then the attribute must not be in a MUST
  5. We should not remove a value of an attribute if it's a part of the entry RDN
  6. If the attribute has a COLLECTIVE flag set, we just ignore it
  7. in the special case of H-R attributes, we must check that their values are String or at least, byte[] representing a String UTF-8 encoded. In this case, we will transform the byte[] to a String

If one of those conditions is not respected, we will throw a NamingException. All those checks are done when we have applied all the modifications : we can have a sequence like :

  • delete all values from attribute A, A is in MUST
  • add value Y to attribute A
    which is valid. If we throw an exception after the deletion, then we can't apply the addition.

It has to be noted that the values must have been normalized in the Normalizer interceptor before being processed by the schema service interceptor.

We have to deal with a special attribute : ObjectClass. Deletion of an objectClass is a little bit more complicated. We have to check different conditions :

  1. The objectClass must exists in the schema
  2. The objectClass must exists in the entry
  3. We can't remove all the values for this attribute
  4. We can't remove an objectClass if one of it's MUST attribute is still present in the entry
  5. We can't remove an objectClass if another objectClass has the first one as a superior
  6. If the objectClass to be removed is ExtensibleObject, then we must check that the remaining attributes are all in the allowed attributes by the remaining ObjectClasses

Add attribute modify operation

The user has requested an adition of an attribute or of a value in an attribute.

RFC 2251 tells :

"add values listed to the given attribute, creating the attribute if necessary"

We have to keep a track of all the errors we met while applying this modification :

  • if the attribute has the COLLECTIVE flag set
  • if the attribute has the NO_USER_MODIFICATION flag set
  • if the attribute is not an allowed attributes
  • if the attribute already exists with another value and the attribute is single valued

 If any of these error occurs, we create an exception and add it to the stack of exception. At the end, if the entry is incorrect, then we will retrn this stack to the user.

Replace attribute modify operation

The user  request a replacement of an attribute's values

RFC 2251 tells :

"replace all existing values of the given attribute with the new values listed, creating the attribute if it did not already exist. A replace with no value will delete the entire attribute if it exists, and is ignored if the attribute does not exist"

We have to keep a track of all the errors we met while applying this modification :

  • if the attribute has the COLLECTIVE flag set
  • if the attribute has the NO_USER_MODIFICATION flag set
  • if the attribute does not exist in the schema
  • if one of the value does not exist
  • if the attribute is in MUST and we try to delete it (no values in the new attribute)
  • if the attribute is objectClass and we try to remove top

If any of these error occurs, we create an exception and add it to the stack of exception. At the end, if the entry is incorrect, then we will retrn this stack to the user.

Delete attribute modify operation

The user has requested a deletion of an attribute or of one or more value.

RFC 2251 tells :
"delete values listed from the given attribute, removing the entire attribute if no values are listed, or if all current values of the attribute are listed for deletion"

We have to keep a track of all the errors we met while applying this modification :

  • if the attribute does not exist
  • if the value does not exist
  • if the attribute is in MUST
  • if the attribute has the COLLECTIVE flag set
  • if the attribute has the NO_USER_MODIFICATION flag set
  • if the attribute is objectClass and we try to remove top

If any of these error occurs, we create an exception and add it to the stack of exception. At the end, if the entry is incorrect, then we will retrn this stack to the user.

search operation

The search operation is associated with the SearchRequest operation.

helper methods

The checks are implemented as private methods into the SchemaService class. Here is the list and description of those methods :

Method

Used

Description

alterObjectClasses

modify, check

 

assertAllAttributesAllowed

check

 

assertHumanReadible

check

Check that if an attributeType is H-R, then all of its values are either String, otherwise transform the byte[] to
a valid String, or throw an exception.

assertNumberOfAttributeValuesValid

computeSuperiors

 

assertNumberOfAttributeValuesValid

computeSuperiors

 

assertRequiredAttributesPresent

check

 

check

add, modify

 

computeMayAttributes

computeSuperiors

 

computeMustAttributes

computeSuperiors

 

computeOCSuperiors

computeSuperiors, computeOCSuperiors

 

computeSuperiors

init

 

filterAttributesToReturn

search

 

filterBinaryAttributes

lookup, BinaryAttributeFilter

 

filterObjectClass

lookup, TopFilter

 

getAllAllowed

check

 

getAllMust

check

 

getObjectClasses

filterObjectClass, check

 

getResultantObjectClasses

modify

 

getSubschemaEntry

search

 

getSuperiors

getObjectClasses, getSuperiors

 

isCompleteRemoval

modify

 

isRequired

modify

 

  • No labels