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.