| Title: 2.6 - Modifying entries |
| NavPrev: 2.5-deleting.html |
| NavPrevText: 2.5 - Deleting entries |
| NavUp: 2-basic-ldap-api-usage.html |
| NavUpText: 2 - Basic LDAP API usage |
| NavNext: 2.7-moving-renaming.html |
| NavNextText: 2.7 - Moving an renaming entries |
| Notice: Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| . |
| http://www.apache.org/licenses/LICENSE-2.0 |
| . |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| # 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 : |
| |
| :::Java |
| 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 : |
| |
| :::Java |
| ... |
| 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 : |
| |
| :::Java |
| ... |
| 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 : |
| |
| :::Java |
| ... |
| 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 : |
| |
| :::Java |
| 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: |
| |
| |
| :::Java |
| ... |
| 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: |
| |
| :::Java |
| 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 : |
| |
| :::Java |
| ... |
| 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: |
| |
| :::Java |
| ... |
| 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. |
| |