Bug 58261 - implicit conversion of DN in oxDeputyPermissionGivenTo leads to errors
Summary: implicit conversion of DN in oxDeputyPermissionGivenTo leads to errors
Status: VERIFIED FIXED
Alias: None
Product: UCS
Classification: Unclassified
Component: UDM (Generic)
Version: UCS 5.2
Hardware: Other Linux
: P5 normal
Target Milestone: UCS 5.2-1-errata
Assignee: Johannes Königer
QA Contact: Dirk Wiesenthal
URL:
Keywords:
Depends on:
Blocks: 58312
  Show dependency treegraph
 
Reported: 2025-05-12 12:16 CEST by Tim Breidenbach
Modified: 2025-05-22 13:22 CEST (History)
5 users (show)

See Also:
What kind of report is it?: Bug Report
What type of bug is this?: 5: Major Usability: Impairs usability in key scenarios
Who will be affected by this bug?: 1: Will affect a very few installed domains
How will those affected feel about the bug?: 5: Blocking further progress on the daily work
User Pain: 0.143
Enterprise Customer affected?: Yes
School Customer affected?:
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number:
Bug group (optional):
Customer ID: 273443
Max CVSS v3 score:
best: Patch_Available+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tim Breidenbach univentionstaff 2025-05-12 12:16:20 CEST
For objects with "+" in the DN (uid=test_user,cn=users,ou=+1,dc=ucs,dc=test) we we save them in the LDAP like "uid=test_user,cn=users,ou=\2B1,dc=ucs,dc=test".

When we use the oxDeputyPermissionGivenTo property we save the DN of a user to it.
When we use the encoding with "\2B" (as it is in LDAP) it gets automaticylly converted by UDM to "uid=test_user,cn=users,ou=\+1,dc=ucs,dc=test".

Later when the ox-connector sets the permissions it uses the DN for a search in the database and can't find the correct entry due to the mismatch "\+" vs "\2B".

Most likely this applies to other special character in DNs.

This might be connected to display issues in the UMC where the "Stellvertreter" field is visually empty.  


We debugged it directly in a python shell as follows:

>>>from univention.udm import UDM
>>>user_mod = UDM.admin().version(2).get("users/user")
>>>fupo_user = user_mod.get_by_id("fupo_user_isOxUser_01")
>>>fupo_user.props.oxDeputyPermissionGivenTo = [['uid=user_isOxUser_02,cn=users,ou=\\2B1,dc=ucs,dc=test', '02400', '02440', '1']]
>>>fupo_user.save()
>>>fupo_user = user_mod.get_by_id("fupo_user_isOxUser_01")
>>>fupo_user.props.oxDeputyPermissionGivenTo
[['uid=user_isOxUser_02,cn=users,ou=\\+1,dc=ucs,dc=test', '02400', '02440', '1']]

whereas ldapsearch results in "\2B":
# univention-ldapsearch -LLL uid="fupo_user_isOxUser_01" dn oxDeputyPermissionGivenTo
dn: uid=fupo_user_isOxUser_01,cn=users,ou=\2B1,dc=ucs,dc=test
oxDeputyPermissionGivenTo: uid=user_isOxUser_02,cn=users,ou=\+1,dc=ucs,dc=test |:$:| 02400 |:$:| 02440 |:$:| 1

# univention-ldapsearch -LLL uid="user_isOxUser_02" dn oxDeputyPermissionGivenTo
dn: uid=user_isOxUser_02,cn=users,ou=\2B1,dc=ucs,dc=test
Comment 2 Florian Best univentionstaff 2025-05-12 15:23:27 CEST
We have to do LDAP DN normalization in certain places in UDM:

>>> ldap.dn.dn2str(ldap.dn.str2dn(r'uid=user_isOxUser_02,cn=users,ou=\+1,dc=ucs,dc=test'))
'uid=user_isOxUser_02,cn=users,ou=\\+1,dc=ucs,dc=test'
>>> ldap.dn.dn2str(ldap.dn.str2dn(r'uid=user_isOxUser_02,cn=users,ou=\2B1,dc=ucs,dc=test'))
'uid=user_isOxUser_02,cn=users,ou=\\+1,dc=ucs,dc=test'
Comment 3 Florian Best univentionstaff 2025-05-12 15:28:57 CEST
Both DN's are valid and equal.

# univention-ldapsearch -LLLb 'uid=foobar,ou=\2B1,dc=ucs,dc=test' 1.1 univentionObjectType
dn: uid=foobar,ou=\2B1,dc=ucs,dc=test
univentionObjectType: users/user

# univention-ldapsearch -LLLb 'uid=foobar,ou=\+1,dc=ucs,dc=test' 1.1 univentionObjectType
dn: uid=foobar,ou=\2B1,dc=ucs,dc=test
univentionObjectType: users/user

> Later when the ox-connector sets the permissions it uses the DN for a search in the database and can't find the correct entry due to the mismatch "\+" vs "\2B".

So how does the connector do its search?
Comment 4 Florian Best univentionstaff 2025-05-12 15:40:38 CEST
Patch suggestion, please test:

diff --git app/listener_trigger app/listener_trigger
index 9f34e43..a0d0414 100755
--- app/listener_trigger
+++ app/listener_trigger
@@ -42,6 +42,8 @@ from collections import defaultdict
 from datetime import datetime
 from pathlib import Path
 
+import ldap.dn
+
 from univention.ox.provisioning import helpers, run
 from univention.ox.provisioning.key_value_store import KeyValueStore
 
@@ -68,9 +70,15 @@ logger.addHandler(handler)
 # logger.setLevel(logging.DEBUG)
 # logger.addHandler(handler)
 
+def normalize_dn(dn):
+    return ldap.dn.dn2str(ldap.dn.str2dn(dn))
+
+
 def _get_old_object(distinguished_name):
-    path_to_old_user = mapping.get(distinguished_name.lower())
+    path_to_old_user = mapping.get(normalize_dn(distinguished_name))
     # be backwards compatible
+    if not path_to_old_user:
+        path_to_old_user = mapping.get(distinguished_name.lower())
     if not path_to_old_user:
         path_to_old_user = mapping.get(distinguished_name)
     if not path_to_old_user:
@@ -333,8 +341,8 @@ def objects_from_files(delete_files=True, move_files=False):
                 logger.info("Object was deleted. Deleting %s", path)
                 path.unlink()
                 if obj.old_distinguished_name:
-                    mapping.set(obj.old_distinguished_name.lower(), None)
-                mapping.set(obj.distinguished_name.lower(), None)
+                    mapping.set(normalize_dn(obj.old_distinguished_name)), None)
+                mapping.set(normalize_dn(obj.distinguished_name), None)
             else:
                 logger.info("mv %s -> %s", path, old_file_path)
                 old_file_path.parent.mkdir(parents=True, exist_ok=True)
@@ -345,8 +353,8 @@ def objects_from_files(delete_files=True, move_files=False):
                     )
                     obj.dump(old_file_path)
                 if obj.old_distinguished_name and obj.old_distinguished_name != obj.distinguished_name:
-                    mapping.set(obj.old_distinguished_name.lower(), None)
-                mapping.set(obj.distinguished_name.lower(), old_file_path)
+                    mapping.set(normalize_dn(obj.old_distinguished_name)), None)
+                mapping.set(normalize_dn(obj.distinguished_name), old_file_path)
         elif delete_files:
             logger.info("Deleting %s", path)
             path.unlink()
Comment 5 Florian Best univentionstaff 2025-05-12 15:41:48 CEST
err, change into:

+def normalize_dn(dn):
+    return ldap.dn.dn2str(ldap.dn.str2dn(dn)).lower()
Comment 6 Johannes Königer univentionstaff 2025-05-12 16:05:09 CEST
Instead of fixing it in the OX-Connector, shouldn't the mechanism ( https://docs.software-univention.de/app-center/5.2/en/identity_management.html#automatically-via-idm-notifications-push ) that writes the original JSON files normalize the DN?
Comment 7 Florian Best univentionstaff 2025-05-12 16:27:33 CEST
(In reply to Johannes Königer from comment #6)
> Instead of fixing it in the OX-Connector, shouldn't the mechanism (
> https://docs.software-univention.de/app-center/5.2/en/identity_management.
> html#automatically-via-idm-notifications-push ) that writes the original
> JSON files normalize the DN?

yes, that's fine for me and sounds correct. I only looked into the function, which has the concrete error in looking up the file.
Comment 8 Dirk Wiesenthal univentionstaff 2025-05-22 10:33:04 CEST
We fixed this in UDM by changing the DN representation in attributes from "normalized" to "untouched".

univention-directory-manager-modules.yaml
84089a9d4a90 | Bug #58261: Do not normalize DNs in attributes. This led to different string representations between attribute values and the actual DNs of objects, causing problems in UMC not finding the already set items in ComboBoxes and made lookups error prone when using different data structures than LDAP (e.g., database cache).

univention-directory-manager-modules (17.0.37)
84089a9d4a90 | Bug #58261: Do not normalize DNs in attributes. This led to different string representations between attribute values and the actual DNs of objects, causing problems in UMC not finding the already set items in ComboBoxes and made lookups error prone when using different data structures than LDAP (e.g., database cache).

ucs-test (12.1.14)
84089a9d4a90 | Bug #58261: Do not normalize DNs in attributes. This led to different string representations between attribute values and the actual DNs of objects, causing problems in UMC not finding the already set items in ComboBoxes and made lookups error prone when using different data structures than LDAP (e.g., database cache).
Comment 11 Dirk Wiesenthal univentionstaff 2025-05-22 10:42:39 CEST
Code review: OK
YAML: OK
Tests: Exist, pass, OK
Changes itself: ~OK, strictly speaking this would require a data migration. But it is only necessary in case such objects exist (e.g., an object below a ou=+1) and is referenced with its DN somewhere else. This is most probably not the case, as the problems would have shown up before:
  * Problems in users (primary group)
  * Problems in groups (users)
  * ... (e.g., portal objects like tiles in categories)

And even then it may only be a problem if you want to work with these values. Maybe you can even get away with those values being in a slightly misaligned format. So, in conclusion, I do not think we need a data migration for every customer.