2.6 - Modifying entries

There are several ways an entry can be modified. Mainly, it’s about adding or deleting an attribute, or modifying the values associated with an existing attribute.

It’s important to understand that many modifications can be applied on a single entry. All those modifications will be applied in an all or none fashion. i.e., if any of the modifications are invalid, none will occur. Also if the server crashes while applying the mods, it’s guaranteed that the entry remains consistent.

How it works?

Each modification to be applied on an entry is encapsulated into an intermediate class : a Modification instance, which can be created as :

    Modification addedGivenName = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName", "John", "Peter" );

Here the modification instance represents addition of values “John” and “Peter” to the giveName attribute (the givenName attribute can have more than one value).

There are four different kind of modifications:

  • ModificationOperation.ADD_ATTRIBUTE: add an attribute and values to an entry
  • ModificationOperation.REMOVE_ATTRIBUTE: remove an attribute and values from an entry
  • ModificationOperation.REPLACE_ATTRIBUTE: replace some existing values from an entry
  • ModificationOperation.INCREMENT_ATTRIBUTE: increment an Integer value of an attribute

Adding or removing full attributes

The following two operations completely add or remove attributes.

Adding new attributes

First of all, let’s learn how to add an attribute. First you must know which entry to modify, which means you must know its Dn. Next, you must create the Modification instance which is applied to the entry. Here is the code that is used to add a givenName attribute to an existing entry :

    ...
    Modification addedGivenName = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName",
        "John", "Peter" );

    connection.modify( "uid=Doe,dc=acme,dc=com", addedGivenName );
    ...

Adding more than one attribute

What if you want to apply more than one modification to the entry ?

Easy : create more than one Modification instance, and add them before calling the modify method :

    ...
    Modification addedGivenName = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName",
        "John", "Peter" );
    Modification addedInitials = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "initials",
        "JD" );

    connection.modify( "uid=Doe,dc=acme,dc=com", addedGivenName, addedInitials );
    ...

You can add as many Modification instances as needed.

Errors

If you try to add an attribute that already exists, you will get an error, like this one :

    ...
    Modification addedGivenName = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName",
        "John", "Peter" );
    Modification addedUid = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "uid",
        "Ted" );

    connection.modify( "uid=Doe,dc=acme,dc=com", addedGivenName, addedUid );
    ...

results in :

    org.apache.directory.api.ldap.model.exception.LdapAttributeInUseException: ATTRIBUTE_OR_VALUE_EXISTS: failed for MessageType : MODIFY_REQUEST
    Message ID : 3
        Modify Request
            Object : 'uid=Doe,dc=acme,dc=com'
                Modification[0]
                    Operation :  add
                    Modification
                        givenName: John
                        givenName: Peter            
                Modification[1]
                    Operation :  add
                    Modification
                        uid: Ted
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@13532916: ERR_54 Cannot add a value which is already present : admin
    at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2064)
    at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
    at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
    ...

Here, we’ve tried to add the uid attribute that already exists, and the error trace tells us as much.

Another potential error occurs when adding an attribute type that isn’t allowed on that entry. This occurs because the Entry’s ObjectClass does not allow such an attribute (per the schema), or because the server forbids modification of that entry, due to the ACIs applied on this entry.

Last, but not least, and hopefully this is obvious, the entry must exist prior to modification!

Removing an attribute

Removing an attribute is actually a bit simpler than adding one, because the values for the attribute don’t need to be specified. Here’s an example where the givenName attribute is removed from an entry:

    ...
    Modification deletedGivenName = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, "givenName" );

    connection.modify( "uid=Doe,dc=acme,dc=com", adeletedivenName );
    ...

Here, we’ve created a modification, specifying the givenName attribute must be removed, and we apply the modification request to the entry.

Again, more than one attribute may be deleted from an entry. It’s a matter of creating more than one Modification instances and applying them to the entry.

Errors

If you try to delete an attribute that does not exist in the entry, you will get this error:

    org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException: NO_SUCH_ATTRIBUTE: failed for MessageType : MODIFY_REQUEST
    Message ID : 3
        Modify Request
            Object : 'uid=admin,ou=system'
                Modification[0]
                    Operation :  delete
                    Modification
                        givenName: (null)
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@fbe6f598: ERR_55 Trying to remove an non-existant attribute: 
    attributetype ( 2.5.4.42 NAME ( 'givenName' 'gn' )
        DESC 'RFC2256: first name(s) for which the entity is known by'
        SUP name
        EQUALITY caseIgnoreMatch
        SUBSTR caseIgnoreSubstringsMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
        USAGE userApplications
    )
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2057)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttribute(ClientModifyRequestTest.java:302)

Here, the entry does not contain the givenName attribute.

Another potential error is when you trying to remove a mandatory attribute, per the entry’s ObjectClass constraints.

Otherwise, the server might forbid modification of entry due to the ACIs that are applicable to it.

Again the entry must exist prior to performing the modification!

Adding, removing or replacing attribute values

You can now update an attribute’s values themselves, atomically, instead of removing the full attribute, and add it back but with updated values. We use the exact same Modification instance, with the same three ModificationOperation, except that the semantics slightly differ.

Typically, this is what happens when using one of the three ModificationOperation on an attribute:

  • ModificationOperation.ADD_ATTRIBUTE : add values to an attribute. If the Attribute does not exist, it will be added
  • ModificationOperation.REMOVE_ATTRIBUTE : remove values from an attribute.
  • ModificationOperation.REPLACE_ATTRIBUTE : replace all the values from an attribute by the provided new values

Add values

Let’s see with the addition of values. Here, we will assume we have an entry like:

    dn: uid=jDoe,dc=acme,dc=com
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    uid: jDoe
    userPassword: secret
    sn: John Tom Doe
    cn: Doe
    givenName: John

We will add the ‘Tom’ given name to the givenName attribute in this entry :

    ...
    Modification addedGivenNameValue = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, "givenName", "Tom" );

    connection.modify( "uid=Doe,dc=acme,dc=com", addedGivenNameValue );
    ...

The entry now has two values for the giveName attribute :

    dn: uid=jDoe,dc=acme,dc=com
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    uid: jDoe
    userPassword: secret
    sn: John Tom Doe
    cn: Doe
    givenName: John
    givenName: Tom

Errors

Again, such an operation might fail for many reasons. Let’s see what are the possible errors:

First, if the attribute’s value already exists. You will get an error like this:

    org.apache.directory.api.ldap.model.exception.LdapAttributeInUseException: ATTRIBUTE_OR_VALUE_EXISTS: failed for MessageType : 
    MODIFY_REQUEST
    Message ID : 5
        Modify Request
            Object : 'uid=admin,ou=system'
                Modification[0]
                    Operation :  add
                    Modification
                        givenName: John
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@867e79fe: ERR_54 Cannot add a value which is already present : John
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2064)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttribute(ClientModifyRequestTest.java:303)

Note that depending on the attribute’s syntax, you may get this type of error because you entered a value with different casing when the syntax is case-insensitive. Typically, if the attribute contains the value ‘John’ and you try to add the value ‘JOHN’, you will get this error message. Be sure you know what the attribute syntax allows you to do…

Second, if the attribute is single valued, it will not be possible to add another value to it. When this happens you’ll get the following error message:

    org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException: CONSTRAINT_VIOLATION: failed for MessageType : MODIFY_REQUEST
    Message ID : 3
        Modify Request
            Object : 'c=FR,ou=users,ou=system'
                Modification[0]
                    Operation :  add
                    Modification
                        c: US
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@cdf2ed2f: ERR_278 More than one value has been provided for the single-valued attribute: c
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2127)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttribute(ClientModifyRequestTest.java:297)

Third, the ACIs set on the server may not allow updating an entry or its attribute.

Removing values

Removing values follow the same pattern. First select the entry, choose its attribute, and list the values to be removed from it. Here is an exemple:

    ...
    Modification removedGivenNameValue = new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, "givenName", "Tom" );

    connection.modify( "uid=Doe,dc=acme,dc=com", removedGivenNameValue );
    ...

The value ‘Tom’ just added should now be removed from the givenName attribute, but the value ‘John’ should still be present.

How do we remove the last value of an attribute? It’s quite simple. The attribute itself must be removed from the entry, if this is allowed (see below).

Errors

There are more potential erros with this operation. Let’s list them all.

First, the value you want to remove does not exist. You will get such an error:

    org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException: NO_SUCH_ATTRIBUTE: failed for MessageType : 
    MODIFY_REQUEST
    Message ID : 5
        Modify Request
            Object : 'uid=admin,ou=system'
                Modification[0]
                    Operation :  delete
                    Modification
                        givenName: Pete
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@39800276: ERR_56 Cannot remove an absent value from attribute : attributetype ( 2.5.4.42 NAME ( 'givenName' 'gn' )
        DESC 'RFC2256: first name(s) for which the entity is known by'
        SUP name
        EQUALITY caseIgnoreMatch
        SUBSTR caseIgnoreSubstringsMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
        USAGE userApplications
    )
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2057)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttributeValue(ClientModifyRequestTest.java:327)

Second, if you try to remove the last value of an attribute which was declared mandatory (in the schema) the following error will occur:

    org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException: OBJECT_CLASS_VIOLATION: failed for MessageType : 
    MODIFY_REQUEST
    Message ID : 3
        Modify Request
            Object : 'uid=billyd,ou=users,ou=system'
                Modification[0]
                    Operation :  delete
                    Modification
                        sn: billyd
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@5e80ddb2: ERR_279 Required attributes [sn(2.5.4.4)] not found within entry uid=billyd,ou=users,ou=system
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2081)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttributeValue(ClientModifyRequestTest.java:314)

Here, we tried to remove the sn attribute’s last value from an entry where it’s a required attribute.

Third, if you try to remove a value which was used as part of the entry’s RDN : which is never allowed. Typically, for the ‘uid=billyd,ou=users,ou=system’ entry, you can’t remove the ‘billyd’ value from the ‘uid’ attribute. This is the error that occurs when such a modification is attempted:

    org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException: NOT_ALLOWED_ON_RDN: failed for MessageType : 
    MODIFY_REQUEST
    Message ID : 3
        Modify Request
            Object : 'uid=billyd,ou=users,ou=system'
                Modification[0]
                    Operation :  delete
                    Modification
                        uid: billyd
    org.apache.directory.api.ldap.model.message.ModifyRequestImpl@4d149d78: ERR_62 Entry uid=billyd,ou=users,ou=system does not have the uid attributeType, which is part of the RDN";
        at org.apache.directory.api.ldap.model.message.ResultCodeEnum.processResponse(ResultCodeEnum.java:2081)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2300)
        at org.apache.directory.ldap.client.api.LdapNetworkConnection.modify(LdapNetworkConnection.java:2309)
        at org.apache.directory.shared.client.api.operations.ClientModifyRequestTest.testModifyRemoveAttributeValue(ClientModifyRequestTest.java:314)

There are also classical errors, when ACLs forbid the removal of a value, or the entry doesn’t exist.

Replace values

Here, what we want to do is to replaces all the values from an attribute with some new values. All in all, this is equivalent to first remove the values, then inject the new values, using modifications, except that in some cases, doing so will not work.

An example where such an operation is mandatory is when replacing the values of a mandatory attribute with something different: with two successive operations, that would fail. A replace will work.

What is important here is that the operation simply replace all the existing values* with new ones, removing the previous ones.

Let’s see how it works with a simple example, with an entry containing:

    dn: uid=jDoe,dc=acme,dc=com
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    uid: jDoe
    userPassword: secret
    sn: John Tom Doe
    cn: Doe
    givenName: John
    givenName: Peter

We will try to replace the givenName:

    Modification replaceGn = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, "givenName",
        "Jack" );

    connection.modify( "uid=jDoe,dc=acme,dc=com", replaceGn );

The modified entry will have its givenName value replaced by ‘Jack’, the two previous values will have been removed.

There is one corner case with this operation: creating a Modification instance where the attribute has no value, then the attribute will be removed from the entry.

Errors

You will get the same errors seen in the two previous operation (ADD and REMOVE) for the very same use cases. Here are some more things to watch out for:

  • never inject more than one value in a SINGLE_VALUE attribute
  • never remove a value which is used by the RDN
  • never delete all the values of a mandatory attribute
  • always have the right to modify the entry
  • never try to update a non-existent entry

Increment attribute

This feature will only work with servers supporting the feature. It can be checked by reading the rootDSE supportedFeatures attribute, which should contain the 1.3.6.1.1.14 value.

The idea is to make it possible to increment an integer attribute in one single operation, instead of reading the entry first, and modify the value in a second operation. That makes the increment operation atomic, and faster as only one operation will be necessary.

Four methods are available in the ModifyRequest operation:

  • ModifyRequest increment( String attribute ): Increment by 1 the attribute value
  • ModifyRequest increment( Striung attribute, int ): Increment by N the attribute value
  • ModifyRequest increment( Attribute attribute ): Increment by 1 the attribute value
  • ModifyRequest increment( Attribute attribute, int ): Increment by N the attribute value

As you can see, it’s possible to increment the value by more than 1 (which is the default).

Note that if the increment attribute is muklti-valued, then all the values will be incremented.