2.6 - Modifying entries

There are several ways an entry can be modified. Mainly, it's about adding or deleting an attribute, or it's about 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 a all or none fashion. For instance, if any of the modifications is invalid, then none of the modifications will be applied. Also if the server crashes while applying the modifications, it's guaranteed that the entry will remain consistent.

How it works ?

Each modification to be applied on an entry is encapsulated in 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 three 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

Adding or removing full attributes

The two following operations are dealing with complete addition or removal of attributes.

Adding new attributes

First of all, let's see how we proceed when it comes to adding an attribute. You need to know the entry you want to modify, which means you have to know its Dn. Then, you have to create the Modification instance that will be applied on 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 have tried to add the uid attribute which already exists, and the error trace says so as expected.

Anothe potential error you can get is when you try to add an attribute that is not allowed in an entry. This can be because the Entry's ObjectClass does not allow such an attribute, or because the server forbid you to modify the entry, due to the ACIs applied on this entry.

Last, not least, but this is quite obvious, the entry must exist !

Removing an attribute

Removing an attribute is actually a bit simpler than adding an attribute, as you don't have to specify the values of the attribute to remove. Here is an example where we will remove the giveName attrinute from an entry :

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

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

Here, we have created a modification, specifying that the givenName attribute has to be removed, and we applied the modification on the entry.

Again, you can delete more than one attribute from an entry, it's just 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.

Anothe potential error you can get is when you try to remove an attribute which is a mandatory attribute, per the entry's ObjectClass constraints.

Otherwise, the server might forbid you to modify the entry, due to the ACIs that are applicable to this entry.

Again the entry must exist before performing a modification!

Adding, removing or replacing attribute values

You can now update an attribute's values themselves, atomically, instead of removing a 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, here is what happens when you use 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, the attribute's value already exists. You will get such an error :

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 such an error because you tried to enter 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 very same error message. Be sure you know wht the attribute syntax allows you to do...

Second, the attribute is single valued : it's not possible to add a second value in the Attribute. 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 you have set on the server may not allow you to update an entry or an attribute.

Remove values

Removing values follow the same scheme : select the entry, chose the attribute, and list the values you want to be removed from this attribute. 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' we just have added should now be removed from the givenName attribute, but the value 'John' should still be present.

Whet if you remove the last value of an attribute ? That quite simple : the attribute itself will be removed from the entry, if this is allowed (see below).

Errors

There are more potential erros with this operation. let's list all of them.

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, you try to remove the last value of an attribute which is mandatory You will get such an error :

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, you try to remove a value which is used as the entry's RDN : this is not allowed. Typically, for the 'uid=billyd,ou=users,ou=system' entry, you can't remove the 'billyd' value from the 'uid' attribute. Here is the error you get when you try to apply such a modifcation :

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)

Then you have the classical errors : the ACLs forbids you to remove a velue, or the entry simply does not 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 exemple where such an operation is mandatory is when you want to replace the value of a mandatory attribute with something different : with two successive operation, that would fail. A replace will work.

What is important here is that the operation simply replace all the existing values by 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 : if you create a Modification instances where you specify an Attribute with 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. Be sure you don't :

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