Bug 57630 - UDM hook import conflicts
Summary: UDM hook import conflicts
Status: CLOSED FIXED
Alias: None
Product: UCS
Classification: Unclassified
Component: UDM - Extended Attributes
Version: UCS 5.0
Hardware: Other Linux
: P5 normal
Target Milestone: UCS 5.2-1-errata
Assignee: Florian Best
QA Contact: Arvid Requate
URL: https://git.knut.univention.de/univen...
Keywords:
: 53350 (view as bug list)
Depends on:
Blocks: 58272
  Show dependency treegraph
 
Reported: 2024-09-30 11:03 CEST by Ole Schwiegert
Modified: 2025-06-05 15:13 CEST (History)
4 users (show)

See Also:
What kind of report is it?: Bug Report
What type of bug is this?: 4: Minor Usability: Impairs usability in secondary scenarios
Who will be affected by this bug?: 3: Will affect average number of installed domains
How will those affected feel about the bug?: 2: A Pain – users won’t like this once they notice it
User Pain: 0.137
Enterprise Customer affected?:
School Customer affected?: Yes
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number: 2025050921000086
Bug group (optional):
Customer ID:
Max CVSS v3 score:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ole Schwiegert univentionstaff 2024-09-30 11:03:24 CEST
There is a bug in UDM, which causes the imports from hooks to potentially conflict with each other and result in a traceback.

Steps to reproduce:

Create the two following files:

/usr/lib/python3/dist-packages/univention/admin/hooks.d/import_conflict_hook.py
```
from datetime import datetime

from univention.admin.hook import simpleHook

class ImportConflictHook(simpleHook):
    type = 'simpleHook'
    def hook_ldap_pre_modify(self, obj):
        time=datetime.now()
```

/usr/lib/python3/dist-packages/univention/admin/hooks.d/import_conflict_hook2.py
```
import datetime

from univention.admin.hook import simpleHook

class ImportConflictHook2(simpleHook):
    type = 'simpleHook'
    def hook_ldap_pre_modify(self, obj):
        time=datetime.datetime.now()
```

Create two extended attributes:

```
udm settings/extended_attribute create --set name=myhook1 --set CLIName=myhook1 --set shortDescription="myhook1" --set longDescription="myhook1" --set ldapMapping=univentionFreeAttribute20 --set objectC
lass=univentionFreeAttributes --set module=users/user --set hook=ImportConflictHook --position "cn=custom attributes,cn=univention,$(ucr get ldap/base)"
udm settings/extended_attribute create --set name=myhook2 --set CLIName=myhook2 --set shortDescription="myhook2" --set longDescription="myhook2" --set ldapMapping=univentionFreeAttribute21 --set objectC
lass=univentionFreeAttributes --set module=users/user --set hook=ImportConflictHook2 --position "cn=custom attributes,cn=univention,$(ucr get ldap/base)"
```

Make sure that you kill any udm cli server process before you can trigger the error with:

```
udm users/user modify --dn uid=Administrator,cn=users,$(ucr get ldap/base) --set myhook1="test"
```


We assume that this is caused by the shared global state from the import logic in hooks.py:

```
def import_hook_files() -> None:
    """Load all additional hook files from :file:`.../univention/admin/hooks.d/*.py`"""
    for dir_ in sys.path:
        hooks_d = os.path.join(dir_, 'univention/admin/hooks.d/')
        if os.path.isdir(hooks_d):
            hooks_files = (os.path.join(hooks_d, f) for f in os.listdir(hooks_d) if f.endswith('.py'))
            for fn in hooks_files:
                try:
                    with open(fn, 'rb') as fd:
                        exec(fd.read(), sys.modules[__name__].__dict__)  # noqa: S102
                    log.debug('admin.hook.import_hook_files: importing %r', fn)
                except Exception:
                    log.exception('admin.hook.import_hook_files: loading %r failed', fn)

```
Comment 3 Florian Best univentionstaff 2025-03-21 14:00:49 CET
The relevant error is:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ucsschool/importer/mass_import/mass_import.py", line 117, in import_users
    user_import.delete_users(users_to_delete)  # 0% - 10%
  File "/usr/lib/python3/dist-packages/ucsschool/importer/mass_import/user_import.py", line 498, in delete_users
    success = self.do_delete(user)
  File "/usr/lib/python3/dist-packages/ucsschool/importer/mass_import/sisopi_user_import.py", line 235, in do_delete
    success = user.modify(lo=self.connection)
  File "/usr/lib/python3/dist-packages/ucsschool/importer/models/import_user.py", line 1066, in modify
    lo, validate, move_if_necessary, check_password_policies=True
  File "/usr/lib/python3/dist-packages/ucsschool/lib/models/user.py", line 326, in modify
    return super(User, self).modify(lo=lo, validate=validate, move_if_necessary=move_if_necessary)
  File "/usr/lib/python3/dist-packages/ucsschool/lib/models/base.py", line 619, in modify
    success = self.modify_without_hooks(lo, validate, move_if_necessary)
  File "/usr/lib/python3/dist-packages/ucsschool/importer/models/import_user.py", line 1107, in modify_without_hooks
    return super(ImportUser, self).modify_without_hooks(lo, validate, move_if_necessary)
  File "/usr/lib/python3/dist-packages/ucsschool/lib/models/base.py", line 646, in modify_without_hooks
    self.do_modify(udm_obj, lo)
  File "/usr/lib/python3/dist-packages/ucsschool/lib/models/user.py", line 379, in do_modify
    return super(User, self).do_modify(udm_obj, lo)
  File "/usr/lib/python3/dist-packages/ucsschool/lib/models/base.py", line 681, in do_modify
    udm_obj.modify(ignore_license=1)
  File "/usr/lib/python3/dist-packages/univention/admin/handlers/users/user.py", line 1288, in modify
    return super(object, self).modify(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/univention/admin/handlers/__init__.py", line 693, in modify
    dn = self._modify(modify_childs, ignore_license=ignore_license, response=response, serverctrls=serverctrls)
  File "/usr/lib/python3/dist-packages/univention/admin/handlers/__init__.py", line 1399, in _modify
    ml = self.call_udm_property_hook('hook_ldap_modlist', self, ml)
  File "/usr/lib/python3/dist-packages/univention/admin/handlers/__init__.py", line 1121, in call_udm_property_hook
    changes = func(module, changes)
  File "<string>", line 59, in hook_ldap_modlist
  File "<string>", line 73, in convert_ts_in_list
  File "<string>", line 95, in udm2ldap
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
Comment 4 Florian Best univentionstaff 2025-03-21 14:06:53 CET
Let's not mix in the whole hook module contents but only those which match 
`inspect.getmembers(x, lambda m: inspect.isclass(m) and and issubclass(m, (simpleHook, AttributeHook)) and m not in (simpleHook, AttributeHook))`
Comment 7 Christina Scheinig univentionstaff 2025-05-09 13:40:39 CEST
Second time the customer ran into this. After they upgraded to 5.2-1 their hook, maybe our temporary fix was overwritten, caused the user import to fail again
Comment 8 Arvid Requate univentionstaff 2025-05-12 10:52:11 CEST
Merged after functional QA:

c2643de7579 | restrict namespace of hooks
c26fa798778 | restrict namespace of syntax extension modules
1605c0350a2 | Changelog and advisory

Successful build
Package: univention-directory-manager-modules
Version: 17.0.34
Branch: 5.2-0
Scope: errata5.2-1
Comment 9 Arvid Requate univentionstaff 2025-05-12 12:45:50 CEST
Florian warned that there is a risk of regression for people that didn't
properly import their dependencies in a hook or syntax extension module,
e.g. doing this in a syntax.d/foo.py:

class Foo(string):
    pass

Without "from univention.admin.syntax import string". Those modules
would now fail to load. In the case of syntax extensions I discovered
during QA that there is no log message written into

/var/log/univention/directory-manager-cmd.log

maybe because logging is not yet initialized at the point of import.

Maybe we can try: newbehavior except: log warning and do oldbehavior?
Comment 10 Arvid Requate univentionstaff 2025-05-12 16:04:56 CEST
First only restrict namespace of hook extension modules,
as syntax extensions may currently depend on implicit imports.

76711fe09e3 | revert(udm): restrict namespace of syntax extension modules

Package: univention-directory-manager-modules
Version: 17.0.35
Branch: 5.2-0
Scope: errata5.2-1
Comment 11 Arvid Requate univentionstaff 2025-05-14 13:58:12 CEST
Verified:
* Code review & functional test
* CI tests
Comment 12 Christian Castens univentionstaff 2025-05-15 12:38:00 CEST
<https://errata.software-univention.de/#/?erratum=5.2x90>
Comment 13 Florian Best univentionstaff 2025-06-05 15:13:54 CEST
*** Bug 53350 has been marked as a duplicate of this bug. ***