This article describes how to replace values of a multi-valued attribute in a directory database using the ldapmodify tool. For more general information about ldapmodify see the blog post “Using ldapmodify”. For newer versions of this post, see “LDAP: Replace a value of a multi-valued attribute“.
In order to replace a value of a multi-valued attribute, it is necessary to provide the value to be replaced with the value that replaces it. The procedure is: delete the old value and add the new value (these two operations can be accomplished in one connection).
Create an entry
Create the following entry:
#
# This is a test user entry
# in which one value of the
# description attribute
# will be replaced with another value.
#
dn: uid=user.0,ou=People,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
postalAddress: Aaren Atp$91327 Broadway Street$Las Vegas, UT 08103
postalCode: 08103
uid: user.0
employeeNumber: 0
initials: AWA
givenName: Aaren
pager: +1 214 214 4195
mobile: +1 947 007 3231
cn: Aaren Atp
sn: Atp
telephoneNumber: +1 089 907 9947
street: 91327 Broadway Street
homePhone: +1 457 787 9183
l: Las Vegas
mail: user.0@example.com
st: UT
userPassword: password
Note that there is no description attribute in the entry LDIF. Add the entry using ldapmodify, in this case using the legacy OpenLDAP ldapmodify tool and specifying the post-read control:
/usr/bin/ldapmodify -h localhost -p 1389 \
-D 'cn=directory manager' -w password \
-c -a -f user.0.LDIF -e postread=cn,sn,mail
adding new entry "uid=user.0,ou=People,dc=example,dc=com"
control: 1.3.6.1.1.13.2 false ZGgEJnVpZD11c2VyLjAsb3U9UGVvcGxlLGRjPWV4YW1wbGUs
ZGM9Y29tMD4wEQQCY24xCwQJQWFyZW4gQXRwMAsEAnNuMQUEA0F0cDAcBARtYWlsMRQEEnVzZXIuM
EBleGFtcGxlLmNvbQ==
# ==> postread
dn: uid=user.0,ou=People,dc=example,dc=com
cn: Aaren Atp
sn: Atp
mail: user.0@example.com
# <== postread
This is the same command using the modern ldapmodify tool:
ldapmodify --hostname localhost --port 1389 \
--bindDn 'cn=directory manager' --bindPassword password \
-c -a -f user.0.LDIF --postReadAttributes cn,sn,mail
# Processing ADD request for uid=user.0,ou=People,dc=example,dc=com
# ADD operation successful for DN uid=user.0,ou=People,dc=example,dc=com
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# cn: Aaren Atp
# sn: Atp
# mail: user.0@example.com
There is no need to do an old-fashioned ldapsearch for the entry. In fact, it is a terrible idea to add or modify an entry and then search for it immediately on a different connection because the eventual consistency model of replication does not guarantee that the changes would be propagated yet. The post-read request control will retrieve the attributes and send them back in the post-read response control. The post-read control verified that the entry exists – this is my test directory server so I know that no one will remove the entry but me.
Add two description attributes
Create the following file to add a description attribute with two values:
>
#
# Adds two values to the
# description attribute
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
add: description
description: description 1
-
add: description
description: description 2
alternatively:
#
# Adds two values to the
# description attribute
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
add: description
description: description 1
description: description 2
Modify the entry using the ldapmodify tool:
ldapmodify --hostname localhost --port 1389 \
--bindDn 'cn=directory manager' --bindPassword password \
-c -a -f replace-mv.LDIF --preReadAttributes description \
--postReadAttributes description
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 1
# description: description 2
Before the modification, there was no description attribute, after the modification, description had two values.
Replace one of the values of the multi-valued attribute
Create the following LDIF to replace description 1 with description 3:
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: description 1
-
add: description
description: description 3
There are two operations, delete value description 1, and then add description 3.
ldapmodify --hostname localhost --port 1389 \
--bindDn 'cn=directory manager' --bindPassword password \
--preReadAttributes description \
--postReadAttributes description -c -a -f change-description.LDIF
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 1
# description: description 2
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 2
# description: description 3
Delete All Values of a Multi-valued Attribute
#
# This LDIF input deletes all values
# of the description attribute.
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description
Delete Selected Values of a Multi-valued Attribute
To delete selected values of a multi-valued attribute:
#
# This LDIF input deletes two values
# of the description attribute,
# namely, "value 1" and "value 2".
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: value 1
description: value 2
These examples assume the directory server has a naming context or prefix dc=example,dc=com. To determine which naming contexts your directory server supports, execute the following query:
ldapsearch -h HOSTNAME -p PORT \ -b "" -s base '(&)' namingContexts
For more information, see the entry “The Root DSE”.
Using Java
In Java using the UnboundID LDAP SDK:
package samplecode;
import java.util.ArrayList;
import java.util.List;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ReadOnlyEntry;
import com.unboundid.ldap.sdk.controls.PostReadRequestControl;
import com.unboundid.ldap.sdk.controls.PostReadResponseControl;
import com.unboundid.ldap.sdk.controls.PreReadRequestControl;
import com.unboundid.ldap.sdk.controls.PreReadResponseControl;
/**
* An example of how to replace values of multi-valued attributes.
*
* @author Terry.Gardner@UnboundID.COM
* @since 0.1
*/
public final class ReplaceValue {
/**
* @param ldapConnectionPool
* Provides services applicable to a pool of connections to a
* directory server. This object may not be {@code null}.
*
* @param entry
* The entry that will be the target of modify requests.
*
* @return A new and distinct ReplaceValue object.
*/
public static ReplaceValue
getReplaceValue(final LDAPConnectionPool ldapConnectionPool,
final String entry) {
if(ldapConnectionPool == null) {
throw new NullPointerException("A null ldap connection pool object "
+ " violates the contract of this class.");
}
return new ReplaceValue(ldapConnectionPool,entry);
}
private ReplaceValue(final LDAPConnectionPool ldapConnectionPool,
final String entry) {
this.ldapConnectionPool = ldapConnectionPool;
this.entry = entry;
}
void addDescriptionValues() {
/*
* Add a description attribute with two values.
*/
final List<Modification> mods = new ArrayList<Modification>();
mods.add(new Modification(ModificationType.ADD,
"description",
"description 1"));
mods.add(new Modification(ModificationType.ADD,
"description",
"description 2"));
final ModifyRequest modifyRequest = new ModifyRequest(entry,mods);
modifyRequest.addControl(new PreReadRequestControl("description"));
modifyRequest.addControl(new PostReadRequestControl("description"));
try {
final LDAPResult result = ldapConnectionPool.modify(modifyRequest);
if(result.hasResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID)) {
final Control c =
result.getResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID);
final ReadOnlyEntry e = ((PreReadResponseControl)c).getEntry();
System.out.println("the entry pre-modify:" + e);
}
if(result.hasResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID)) {
final Control c =
result.getResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID);
final ReadOnlyEntry e = ((PostReadResponseControl)c).getEntry();
System.out.println("the entry post-modify:" + e);
}
} catch (final LDAPException lex) {
lex.printStackTrace();
}
}
void replaceDescriptionValues() {
/*
* Replace 'description 1' with 'description 3'.
*/
final List<Modification> mods = new ArrayList<Modification>();
mods.add(new Modification(ModificationType.REPLACE,
"description",
"description 1",
"description 3"));
final ModifyRequest modifyRequest = new ModifyRequest(entry,mods);
modifyRequest.addControl(new PreReadRequestControl("description"));
modifyRequest.addControl(new PostReadRequestControl("description"));
try {
final LDAPResult result = ldapConnectionPool.modify(modifyRequest);
if(result.hasResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID)) {
final Control c =
result.getResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID);
final ReadOnlyEntry e = ((PreReadResponseControl)c).getEntry();
System.out.println("the entry pre-modify:" + e);
}
if(result.hasResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID)) {
final Control c =
result.getResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID);
final ReadOnlyEntry e = ((PostReadResponseControl)c).getEntry();
System.out.println("the entry post-modify:" + e);
}
} catch (final LDAPException lex) {
lex.printStackTrace();
}
}
private final String entry;
private final LDAPConnectionPool ldapConnectionPool;
}
Great information, thanks Terry 🙂