Visitar URL original
Allow for Modlist with ordered attribute values · tacerus/python-ldap@2400657 · GitHub
Skip to content

Commit 2400657

Browse files
tacerusspaceone
authored andcommitted
Allow for Modlist with ordered attribute values
In some LDAP implementations attribute ordering is relevant. Allow for modifications which consider the order of attributes by implementing an optional "strict_order_attr_types" parameter which takes a list of attributes to exempt from the transformation to a set. Signed-off-by: Georg Pfuetzenreuter <georg.pfuetzenreuter@suse.com>
1 parent 538da62 commit 2400657

3 files changed

Lines changed: 114 additions & 7 deletions

File tree

‎Doc/reference/ldap-modlist.rst‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The :mod:`ldap.modlist` module defines the following functions:
2020
in the result at all.
2121

2222

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

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

52+
*strict_order_attr_types* is a list of attribute type names for which
53+
value ordering should be considered - differences in the order of values
54+
without a difference in the value contents will be treated as a change
55+
for these attribute types instead of being ignored.
56+
5257
.. note::
5358
Replacing attribute values is always done with a
5459
:py:const:`ldap.MOD_DELETE`/:py:const:`ldap.MOD_ADD` pair instead of

‎Lib/ldap/modlist.py‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def modifyModlist(
4040
ignore_attr_types: list[str] | None = None,
4141
ignore_oldexistent: int = 0,
4242
case_ignore_attr_types: list[str] | None = None,
43+
strict_order_attr_types: list[str] | None = None,
4344
) -> LDAPModifyModList:
4445
"""
4546
Build differential modify list for calling LDAPObject.modify()/modify_s()
@@ -59,9 +60,13 @@ def modifyModlist(
5960
case_ignore_attr_types
6061
List of attribute type names for which comparison will be made
6162
case-insensitive
63+
strict_order_attr_types
64+
List of attribute type names for which comparison will be made
65+
with strict ordering (by default, value ordering changes will be ignored)
6266
"""
6367
ignore_attr_types_set = {v.lower() for v in ignore_attr_types or []}
6468
case_ignore_attr_types_set = {v.lower() for v in case_ignore_attr_types or []}
69+
strict_order_attr_types_set = {v.lower() for v in strict_order_attr_types or []}
6570
modlist: list[LDAPModListModifyEntry] = []
6671
attrtype_lower_map = {}
6772
for a in old_entry:
@@ -87,11 +92,19 @@ def modifyModlist(
8792
replace_attr_value = len(old_value) != len(new_value)
8893
if not replace_attr_value:
8994
if attrtype_lower in case_ignore_attr_types_set:
90-
old_value_set = {v.lower() for v in old_value}
91-
new_value_set = {v.lower() for v in new_value}
95+
if attrtype_lower in strict_order_attr_types_set:
96+
old_value_set = [v.lower() for v in old_value]
97+
new_value_set = [v.lower() for v in new_value]
98+
else:
99+
old_value_set = {v.lower() for v in old_value}
100+
new_value_set = {v.lower() for v in new_value}
92101
else:
93-
old_value_set = set(old_value)
94-
new_value_set = set(new_value)
102+
if attrtype_lower in strict_order_attr_types_set:
103+
old_value_set = [old_value]
104+
new_value_set = [new_value]
105+
else:
106+
old_value_set = set(old_value)
107+
new_value_set = set(new_value)
95108
replace_attr_value = new_value_set != old_value_set
96109
if replace_attr_value:
97110
modlist.extend(((ldap.MOD_DELETE, attrtype, None), (ldap.MOD_ADD, attrtype, new_value)))

‎Tests/t_ldap_modlist.py‎

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def test_addModlist(self):
6464
'mail': [b'michael@stroeder.com'],
6565
},
6666
[],
67+
[],
6768
[
6869
(ldap.MOD_DELETE, 'objectClass', None),
6970
(ldap.MOD_ADD, 'objectClass', [b'person', b'inetOrgPerson']),
@@ -74,6 +75,80 @@ def test_addModlist(self):
7475
(ldap.MOD_ADD, 'enum', [b'a', b'b', b'd']),
7576
],
7677
),
78+
(
79+
{
80+
'objectClass': [b'person', b'pilotPerson'],
81+
'cn': [b'Max Mustermann'],
82+
'sn': [b'Mustermann'],
83+
'mail': [b'max@example.com'],
84+
},
85+
{
86+
'objectClass': [b'person', b'pilotPerson'],
87+
'cn': [b'Max Mustermann'],
88+
'sn': [b'Mustermann'],
89+
'mail': [b'max@example.com'],
90+
},
91+
[],
92+
['mail'], # strict_order_attr_types
93+
[],
94+
),
95+
(
96+
{
97+
'objectClass': [b'person', b'pilotPerson'],
98+
'cn': [b'Max Mustermann'],
99+
'sn': [b'Mustermann'],
100+
'mail': [b'max@example.com'],
101+
},
102+
{
103+
'objectClass': [b'person', b'pilotPerson'],
104+
'cn': [b'Max Mustermann'],
105+
'sn': [b'Mustermann'],
106+
'mail': [b'max@example.com', b'max@example.net'],
107+
},
108+
[],
109+
['mail'], # strict_order_attr_types
110+
[
111+
(ldap.MOD_DELETE, 'mail', None),
112+
(ldap.MOD_ADD, 'mail', [b'max@example.com', b'max@example.net']),
113+
],
114+
),
115+
(
116+
{
117+
'objectClass': [b'person', b'pilotPerson'],
118+
'cn': [b'Max Mustermann'],
119+
'sn': [b'Mustermann'],
120+
'mail': [b'max@example.com', b'max@example.net'],
121+
},
122+
{
123+
'objectClass': [b'person', b'pilotPerson'],
124+
'cn': [b'Max Mustermann'],
125+
'sn': [b'Mustermann'],
126+
'mail': [b'max@example.net', b'max@example.com'],
127+
},
128+
[],
129+
['mail'], # strict_order_attr_types
130+
[
131+
(ldap.MOD_DELETE, 'mail', None),
132+
(ldap.MOD_ADD, 'mail', [b'max@example.net', b'max@example.com']),
133+
],
134+
),
135+
(
136+
{
137+
'objectClass': [b'person', b'pilotPerson'],
138+
'cn': [b'Max Mustermann'],
139+
'sn': [b'Mustermann'],
140+
'mail': [b'max@example.com', b'max@example.net'],
141+
},
142+
{
143+
'objectClass': [b'person', b'pilotPerson'],
144+
'cn': [b'Max Mustermann'],
145+
'sn': [b'Mustermann'],
146+
'mail': [b'max@example.net', b'max@example.com'],
147+
},
148+
[],
149+
[], # strict_order_attr_types
150+
[],
151+
),
77152
(
78153
{
79154
'c': [b'DE'],
@@ -82,6 +157,7 @@ def test_addModlist(self):
82157
'c': [b'FR'],
83158
},
84159
[],
160+
[],
85161
[
86162
(ldap.MOD_DELETE, 'c', None),
87163
(ldap.MOD_ADD, 'c', [b'FR']),
@@ -102,6 +178,7 @@ def test_addModlist(self):
102178
'sn': [None],
103179
},
104180
[],
181+
[],
105182
[
106183
(ldap.MOD_DELETE, 'c', None),
107184
(ldap.MOD_DELETE, 'objectClass', None),
@@ -122,6 +199,7 @@ def test_addModlist(self):
122199
'enum': [b'a', b'b', b'c'],
123200
},
124201
['objectClass'],
202+
[],
125203
[
126204
(ldap.MOD_DELETE, 'sn', None),
127205
(ldap.MOD_DELETE, 'enum', None),
@@ -131,9 +209,20 @@ def test_addModlist(self):
131209
]
132210

133211
def test_modifyModlist(self):
134-
for old_entry, new_entry, case_ignore_attr_types, test_modlist in self.modifyModlist_tests:
212+
for (
213+
old_entry,
214+
new_entry,
215+
case_ignore_attr_types,
216+
strict_order_attr_types,
217+
test_modlist,
218+
) in self.modifyModlist_tests:
135219
test_modlist.sort()
136-
result_modlist = modifyModlist(old_entry, new_entry, case_ignore_attr_types=case_ignore_attr_types)
220+
result_modlist = modifyModlist(
221+
old_entry,
222+
new_entry,
223+
case_ignore_attr_types=case_ignore_attr_types,
224+
strict_order_attr_types=strict_order_attr_types,
225+
)
137226
result_modlist.sort()
138227

139228
self.assertEqual(

0 commit comments

Comments
 (0)