Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Doc/reference/ldap-modlist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The :mod:`ldap.modlist` module defines the following functions:
in the result at all.


.. function:: modifyModlist( old_entry, new_entry [, ignore_attr_types=[] [, ignore_oldexistent=0 [, case_ignore_attr_types=None]]]) -> list
.. function:: modifyModlist( old_entry, new_entry [, ignore_attr_types=[] [, ignore_oldexistent=0 [, case_ignore_attr_types=None, strict_order_attr_types=None]]]) -> list

This function builds a list suitable for passing it directly as argument
*modlist* to method :py:meth:`ldap.ldapobject.LDAPObject.modify` or
Expand Down Expand Up @@ -49,6 +49,11 @@ The :mod:`ldap.modlist` module defines the following functions:
situations where a LDAP server normalizes values and one wants to avoid
unnecessary changes (e.g. case of attribute type names in DNs).

*strict_order_attr_types* is a list of attribute type names for which
value ordering should be considered - differences in the order of values
without a difference in the value contents will be treated as a change
for these attribute types instead of being ignored.

.. note::
Replacing attribute values is always done with a
:py:const:`ldap.MOD_DELETE`/:py:const:`ldap.MOD_ADD` pair instead of
Expand Down
22 changes: 17 additions & 5 deletions Lib/ldap/modlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def addModlist(entry,ignore_attr_types=None):


def modifyModlist(
old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None
old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None, strict_order_attr_types=None,
):
"""
Build differential modify list for calling LDAPObject.modify()/modify_s()
Expand All @@ -45,9 +45,13 @@ def modifyModlist(
case_ignore_attr_types
List of attribute type names for which comparison will be made
case-insensitive
strict_order_attr_types
List of attribute type names for which comparison will be made
with strict ordering (by default, value ordering changes will be ignored)
"""
ignore_attr_types = {v.lower() for v in ignore_attr_types or []}
case_ignore_attr_types = {v.lower() for v in case_ignore_attr_types or []}
strict_order_attr_types = {v.lower() for v in strict_order_attr_types or []}
modlist = []
attrtype_lower_map = {}
for a in old_entry:
Expand All @@ -73,11 +77,19 @@ def modifyModlist(
replace_attr_value = len(old_value)!=len(new_value)
if not replace_attr_value:
if attrtype_lower in case_ignore_attr_types:
old_value_set = {v.lower() for v in old_value}
new_value_set = {v.lower() for v in new_value}
if attrtype_lower in strict_order_attr_types:
old_value_set = [v.lower() for v in old_value]
new_value_set = [v.lower() for v in new_value]
else:
old_value_set = {v.lower() for v in old_value}
new_value_set = {v.lower() for v in new_value}
else:
old_value_set = set(old_value)
new_value_set = set(new_value)
if attrtype_lower in strict_order_attr_types:
old_value_set = [old_value]
new_value_set = [new_value]
else:
old_value_set = set(old_value)
new_value_set = set(new_value)
replace_attr_value = new_value_set != old_value_set
if replace_attr_value:
modlist.append((ldap.MOD_DELETE,attrtype,None))
Expand Down
88 changes: 86 additions & 2 deletions Tests/t_ldap_modlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def test_addModlist(self):
'mail':[b'[email protected]'],
},
[],
[],
[
(ldap.MOD_DELETE,'objectClass',None),
(ldap.MOD_ADD,'objectClass',[b'person',b'inetOrgPerson']),
Expand All @@ -76,6 +77,84 @@ def test_addModlist(self):
]
),

(
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]'],
},
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]'],
},
[],
['mail'], # strict_order_attr_types
[]
),

(
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]'],
},
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]', b'[email protected]'],
},
[],
['mail'], # strict_order_attr_types
[
(ldap.MOD_DELETE,'mail',None),
(ldap.MOD_ADD,'mail',[b'[email protected]', b'[email protected]']),
]
),

(
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]', b'[email protected]'],
},
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]', b'[email protected]'],
},
[],
['mail'], # strict_order_attr_types
[
(ldap.MOD_DELETE,'mail',None),
(ldap.MOD_ADD,'mail',[b'[email protected]', b'[email protected]']),
]
),

(
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]', b'[email protected]'],
},
{
'objectClass':[b'person',b'pilotPerson'],
'cn':[b'Max Mustermann'],
'sn':[b'Mustermann'],
'mail':[b'[email protected]', b'[email protected]'],
},
[],
[], # strict_order_attr_types
[]
),

(
{
'c':[b'DE'],
Expand All @@ -84,6 +163,7 @@ def test_addModlist(self):
'c':[b'FR'],
},
[],
[],
[
(ldap.MOD_DELETE,'c',None),
(ldap.MOD_ADD,'c',[b'FR']),
Expand All @@ -105,6 +185,7 @@ def test_addModlist(self):
'sn':[None],
},
[],
[],
[
(ldap.MOD_DELETE,'c',None),
(ldap.MOD_DELETE,'objectClass',None),
Expand All @@ -126,6 +207,7 @@ def test_addModlist(self):
'enum':[b'a',b'b',b'c'],
},
['objectClass'],
[],
[
(ldap.MOD_DELETE,'sn',None),
(ldap.MOD_DELETE,'enum',None),
Expand All @@ -136,11 +218,13 @@ def test_addModlist(self):
]

def test_modifyModlist(self):
for old_entry, new_entry, case_ignore_attr_types, test_modlist in self.modifyModlist_tests:
for old_entry, new_entry, case_ignore_attr_types, strict_order_attr_types, test_modlist in self.modifyModlist_tests:
test_modlist.sort()
result_modlist = modifyModlist(
old_entry, new_entry,
case_ignore_attr_types=case_ignore_attr_types)
case_ignore_attr_types=case_ignore_attr_types,
strict_order_attr_types=strict_order_attr_types,
)
result_modlist.sort()

self.assertEqual(
Expand Down