[python-ldap] Modlist with a replace sometimes fails

William william at blackhats.net.au
Thu Feb 18 18:23:39 EST 2016

> Please give more details:
> - OpenLDAP version
> - *exact* modify operation
> - *exact* error message
> - preferrably test code illustrating the issue

389-ds-1.3.5-git-master. The replace was replacing nsslapd-cachememsize on the
ldbm backend instance.

It rejects the op, because you can't delete the nsslapd-cachememsize, only

The issue is there are *many* attributes like this in cn=config on 389-ds that
can only be replaced, and block deletes.

I propsed a patch to pyldap yesterday for this:

This leaves the *default* behaviour to be the current delete -> add method. It
adds a boolean flag to the operation allowing someone to select if they want to
use replace instead. 

The reason I do *not* want to manually craft the modlist is that would mean
practically re-implementing all of modlist, as our testing framework has an ldap
entry wrapper. I'm writing it to have a dict of data, then .save() to commit the
object based on modlist.modifyModlist(). Especially for handling our cn=config
entries, the ability to use mod_replace is vital.

diff --git a/Lib/ldap/modlist.py b/Lib/ldap/modlist.py
index 0053a3f..a56c3e5 100644
--- a/Lib/ldap/modlist.py
+++ b/Lib/ldap/modlist.py
@@ -49,7 +49,7 @@ def addModlist(entry,ignore_attr_types=None):
 def modifyModlist(
-  old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_at
+  old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_at
   Build differential modify list for calling LDAPObject.modify()/modify_s()
@@ -69,6 +69,10 @@ def modifyModlist(
       List of attribute type names for which comparison will be made
+  replace_is_add_then_delete
+      Determines if a replace operation is carried out as add then delete
+      or if it is a pure ldap replace. This can have behavioural affects on
+      certain ldap servers and object types IE configuration directories.
   ignore_attr_types = list_dict(map(lower,(ignore_attr_types or [])))
   case_ignore_attr_types = list_dict(map(lower,(case_ignore_attr_types or [])))
@@ -111,8 +115,11 @@ def modifyModlist(
               replace_attr_value = 1
       if replace_attr_value:
-        modlist.append((ldap.MOD_DELETE,attrtype,None))
-        modlist.append((ldap.MOD_ADD,attrtype,new_value))
+        if replace_is_add_then_delete:
+          modlist.append((ldap.MOD_DELETE,attrtype,None))
+          modlist.append((ldap.MOD_ADD,attrtype,new_value))
+        else:
+          modlist.append((ldap.MOD_REPLACE,attrtype,new_value))
     elif old_value and not new_value:
       # Completely delete an existing attribute

William <william at blackhats.net.au>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part
URL: <http://mail.python.org/pipermail/python-ldap/attachments/20160219/946fc8d1/attachment.sig>

More information about the python-ldap mailing list