From 4354c1bc5d64fde9146607bd445676e59f6d26fd Mon Sep 17 00:00:00 2001 From: Andrian Balanescu Date: Mon, 13 Jul 2026 00:48:19 +0000 Subject: [PATCH 1/3] fix: gracefully handle missing ldap.group_search to prevent HTTP 500 (#12225) When LDAP is enabled but ldap.group_search is not configured, django-auth-ldap crashes at runtime with TypeError when performing group permission lookups. This results in HTTP 500 errors during normal API requests. Changes: - Make AUTH_LDAP_FIND_GROUP_PERMS configurable via ldap.find_group_perms setting (defaults to True for backward compatibility) - When group_search DN is None, disable all group-based features (find_group_perms, mirror_groups, require_group, deny_group, user_flags_by_group) gracefully with a warning instead of crashing - Set AUTH_LDAP_GROUP_SEARCH to None instead of LDAPSearch(None, ...) - Add tests for all three scenarios: missing group_search, configured group_search, explicit find_group_perms=False - Document new ldap.find_group_perms setting --- docs/docs/start/advanced.md | 3 +- .../InvenTree/InvenTree/setting/ldap.py | 41 ++++- .../InvenTree/setting/tests/__init__.py | 0 .../setting/tests/test_ldap_config.py | 145 ++++++++++++++++++ 4 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/backend/InvenTree/setting/tests/__init__.py create mode 100644 src/backend/InvenTree/setting/tests/test_ldap_config.py diff --git a/docs/docs/start/advanced.md b/docs/docs/start/advanced.md index 9bacc83924fd..25f6832365d3 100644 --- a/docs/docs/start/advanced.md +++ b/docs/docs/start/advanced.md @@ -65,7 +65,7 @@ Next you can start configuring the connection. Either use the config file or set | `INVENTREE_LDAP_USER_ATTR_MAP` | `ldap.user_attr_map` | LDAP <-> InvenTree user attribute map, can be json if used as env, in yml directly specify the object. default: `{"first_name": "givenName", "last_name": "sn", "email": "mail"}` | | `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | `ldap.always_update_user` | Always update the user on each login, default: `true` | | `INVENTREE_LDAP_CACHE_TIMEOUT` | `ldap.cache_timeout` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) | -| `INVENTREE_LDAP_GROUP_SEARCH` | `ldap.group_search` | Base LDAP DN for group searching; required to enable group features | +| `INVENTREE_LDAP_GROUP_SEARCH` | `ldap.group_search` | Base LDAP DN for group searching; required to enable group features. If not set, all group-based features are disabled gracefully | | `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | `ldap.group_object_class` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` | | `INVENTREE_LDAP_MIRROR_GROUPS` | `ldap.mirror_groups` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` | | `INVENTREE_LDAP_GROUP_TYPE_CLASS` | `ldap.group_type_class` | The group class to be imported from `django_auth_ldap.config` as a string, default: `'GroupOfUniqueNamesType'`| @@ -74,6 +74,7 @@ Next you can start configuring the connection. Either use the config file or set | `INVENTREE_LDAP_REQUIRE_GROUP` | `ldap.require_group` | If set, users _must_ be in this group to log in to InvenTree | | `INVENTREE_LDAP_DENY_GROUP` | `ldap.deny_group` | If set, users _must not_ be in this group to log in to InvenTree | | `INVENTREE_LDAP_USER_FLAGS_BY_GROUP` | `ldap.user_flags_by_group` | LDAP group to InvenTree user flag map, can be json if used as env, in yml directly specify the object. See config template for example, default: `{}` | +| `INVENTREE_LDAP_FIND_GROUP_PERMS` | `ldap.find_group_perms` | If `True`, look up LDAP group permissions during authentication. Automatically disabled when `ldap.group_search` is not set. default: `True` | ## Tracing support diff --git a/src/backend/InvenTree/InvenTree/setting/ldap.py b/src/backend/InvenTree/InvenTree/setting/ldap.py index 3b3917970182..832081cd6858 100644 --- a/src/backend/InvenTree/InvenTree/setting/ldap.py +++ b/src/backend/InvenTree/InvenTree/setting/ldap.py @@ -68,6 +68,33 @@ def get_ldap_config(debug: bool = False) -> dict: str, ) + group_search_dn = get_setting( + 'INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search' + ) + + find_group_perms = get_boolean_setting( + 'INVENTREE_LDAP_FIND_GROUP_PERMS', 'ldap.find_group_perms', True + ) + + # If group search DN is not configured, group-based features cannot + # work. Disable them gracefully with a warning instead of letting + # django-auth-ldap crash at runtime with a TypeError (see #12225). + if group_search_dn is None: + if find_group_perms or get_setting( + 'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups' + ) or get_setting( + 'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group' + ) or get_setting( + 'INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group' + ): + print( + '[LDAP] ldap.group_search is not configured; ' + 'disabling group-based features (find_group_perms, ' + 'mirror_groups, require_group, deny_group). ' + 'Set ldap.group_search to enable them.' + ) + find_group_perms = False + ldap_config = { 'AUTH_LDAP_GLOBAL_OPTIONS': global_options, 'AUTH_LDAP_SERVER_URI': get_setting( @@ -108,13 +135,13 @@ def get_ldap_config(debug: bool = False) -> dict: ), 'AUTH_LDAP_MIRROR_GROUPS': get_boolean_setting( 'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups', False - ), + ) if group_search_dn is not None else False, 'AUTH_LDAP_GROUP_OBJECT_CLASS': group_object_class, 'AUTH_LDAP_GROUP_SEARCH': django_auth_ldap.config.LDAPSearch( - get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search'), + group_search_dn, ldap.SCOPE_SUBTREE, f'(objectClass={group_object_class})', - ), + ) if group_search_dn is not None else None, 'AUTH_LDAP_GROUP_TYPE_CLASS': group_type_class, 'AUTH_LDAP_GROUP_TYPE_CLASS_ARGS': [*group_type_class_args], 'AUTH_LDAP_GROUP_TYPE_CLASS_KWARGS': {**group_type_class_kwargs}, @@ -123,17 +150,17 @@ def get_ldap_config(debug: bool = False) -> dict: ), 'AUTH_LDAP_REQUIRE_GROUP': get_setting( 'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group' - ), + ) if group_search_dn is not None else None, 'AUTH_LDAP_DENY_GROUP': get_setting( 'INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group' - ), + ) if group_search_dn is not None else None, 'AUTH_LDAP_USER_FLAGS_BY_GROUP': get_setting( 'INVENTREE_LDAP_USER_FLAGS_BY_GROUP', 'ldap.user_flags_by_group', default_value=None, typecast=dict, - ), - 'AUTH_LDAP_FIND_GROUP_PERMS': True, + ) if group_search_dn is not None else None, + 'AUTH_LDAP_FIND_GROUP_PERMS': find_group_perms, } return ldap_config diff --git a/src/backend/InvenTree/setting/tests/__init__.py b/src/backend/InvenTree/setting/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/backend/InvenTree/setting/tests/test_ldap_config.py b/src/backend/InvenTree/setting/tests/test_ldap_config.py new file mode 100644 index 000000000000..9f5bb6b8409c --- /dev/null +++ b/src/backend/InvenTree/setting/tests/test_ldap_config.py @@ -0,0 +1,145 @@ +"""Tests for LDAP configuration handling. + +Verifies that missing ``ldap.group_search`` does not cause runtime crashes +and that ``find_group_perms`` is properly configurable. +""" + +import importlib +from unittest import mock + +import pytest + + +@pytest.fixture +def ldap_module(): + """Import the ldap setting module fresh for each test.""" + # The module imports django_auth_ldap and ldap at function level, + # so we can import it directly without those packages installed + # as long as we mock them for the get_ldap_config call. + import InvenTree.InvenTree.setting.ldap as ldap_mod + importlib.reload(ldap_mod) + return ldap_mod + + +class TestLdapGroupSearchMissing: + """Tests for graceful handling of missing ldap.group_search (see #12225).""" + + def test_find_group_perms_disabled_when_group_search_missing(self, ldap_module): + """When group_search is None, find_group_perms must be False.""" + with mock.patch.object(ldap_module, 'get_setting') as gs, \ + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: + + # get_setting: return None for group_search, defaults for others + def gs_side_effect(env_key, yaml_key, *args, **kwargs): + if 'GROUP_SEARCH' in env_key: + return None + if 'GLOBAL_OPTIONS' in env_key: + return kwargs.get('default_value', {}) or {} + if 'GROUP_TYPE_CLASS_ARGS' in env_key: + return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + if 'GROUP_TYPE_CLASS_KWARGS' in env_key: + return kwargs.get('default_value', {}) or {} + return kwargs.get('default_value', None) + + gs.side_effect = gs_side_effect + + # get_boolean_setting: return True for find_group_perms, False for others + def gbs_side_effect(env_key, yaml_key, default): + if 'FIND_GROUP_PERMS' in env_key: + return True + return default + + gbs.side_effect = gbs_side_effect + + # Mock the imports inside get_ldap_config + with mock.patch('django_auth_ldap.config') as mock_config: + mock_config.LDAPSearch.return_value = mock.MagicMock() + mock_config.GroupOfUniqueNamesType.return_value = mock.MagicMock() + + with mock.patch('ldap') as mock_ldap: + mock_ldap.SCOPE_SUBTREE = 2 + mock_ldap.OPT_REFERRALS = 0 + + config = ldap_module.get_ldap_config(debug=False) + + assert config['AUTH_LDAP_FIND_GROUP_PERMS'] is False + assert config['AUTH_LDAP_GROUP_SEARCH'] is None + assert config['AUTH_LDAP_MIRROR_GROUPS'] is False + assert config['AUTH_LDAP_REQUIRE_GROUP'] is None + assert config['AUTH_LDAP_DENY_GROUP'] is None + assert config['AUTH_LDAP_USER_FLAGS_BY_GROUP'] is None + + def test_find_group_perms_enabled_when_group_search_set(self, ldap_module): + """When group_search is configured, find_group_perms stays True.""" + with mock.patch.object(ldap_module, 'get_setting') as gs, \ + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: + + def gs_side_effect(env_key, yaml_key, *args, **kwargs): + if 'GROUP_SEARCH' in env_key: + return 'ou=groups,dc=example,dc=org' + if 'GLOBAL_OPTIONS' in env_key: + return kwargs.get('default_value', {}) or {} + if 'GROUP_TYPE_CLASS_ARGS' in env_key: + return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + if 'GROUP_TYPE_CLASS_KWARGS' in env_key: + return kwargs.get('default_value', {}) or {} + return kwargs.get('default_value', None) + + gs.side_effect = gs_side_effect + + def gbs_side_effect(env_key, yaml_key, default): + if 'FIND_GROUP_PERMS' in env_key: + return True + return default + + gbs.side_effect = gbs_side_effect + + with mock.patch('django_auth_ldap.config') as mock_config: + mock_config.LDAPSearch.return_value = mock.MagicMock() + mock_config.GroupOfUniqueNamesType.return_value = mock.MagicMock() + + with mock.patch('ldap') as mock_ldap: + mock_ldap.SCOPE_SUBTREE = 2 + + config = ldap_module.get_ldap_config(debug=False) + + assert config['AUTH_LDAP_FIND_GROUP_PERMS'] is True + assert config['AUTH_LDAP_GROUP_SEARCH'] is not None + + def test_find_group_perms_can_be_disabled_explicitly(self, ldap_module): + """User can set find_group_perms=False even when group_search is set.""" + with mock.patch.object(ldap_module, 'get_setting') as gs, \ + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: + + def gs_side_effect(env_key, yaml_key, *args, **kwargs): + if 'GROUP_SEARCH' in env_key: + return 'ou=groups,dc=example,dc=org' + if 'GLOBAL_OPTIONS' in env_key: + return kwargs.get('default_value', {}) or {} + if 'GROUP_TYPE_CLASS_ARGS' in env_key: + return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + if 'GROUP_TYPE_CLASS_KWARGS' in env_key: + return kwargs.get('default_value', {}) or {} + return kwargs.get('default_value', None) + + gs.side_effect = gs_side_effect + + def gbs_side_effect(env_key, yaml_key, default): + if 'FIND_GROUP_PERMS' in env_key: + return False + return default + + gbs.side_effect = gbs_side_effect + + with mock.patch('django_auth_ldap.config') as mock_config: + mock_config.LDAPSearch.return_value = mock.MagicMock() + mock_config.GroupOfUniqueNamesType.return_value = mock.MagicMock() + + with mock.patch('ldap') as mock_ldap: + mock_ldap.SCOPE_SUBTREE = 2 + + config = ldap_module.get_ldap_config(debug=False) + + assert config['AUTH_LDAP_FIND_GROUP_PERMS'] is False + # Group search is still configured (user might use it for other features) + assert config['AUTH_LDAP_GROUP_SEARCH'] is not None \ No newline at end of file From a28c78a86934034394ca92c5c5ad3db77be785a3 Mon Sep 17 00:00:00 2001 From: AndrianBalanescu <37745667+AndrianBalanescu@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:51:20 -0500 Subject: [PATCH 2/3] fix: handle empty-string group_search_dn same as None Per review feedback from @SchrodingersGat: an empty-string ldap.group_search value should trigger the same fallback as None, otherwise django-auth-ldap receives an empty DN and crashes. Replace all 'is None' / 'is not None' checks on group_search_dn with truthy checks so '' and None are treated identically. Add test_find_group_perms_disabled_when_group_search_empty covering the empty-string case. --- .../InvenTree/InvenTree/setting/ldap.py | 48 ++++++---- .../setting/tests/test_ldap_config.py | 96 ++++++++++++++++--- 2 files changed, 109 insertions(+), 35 deletions(-) diff --git a/src/backend/InvenTree/InvenTree/setting/ldap.py b/src/backend/InvenTree/InvenTree/setting/ldap.py index 832081cd6858..df7d355099c8 100644 --- a/src/backend/InvenTree/InvenTree/setting/ldap.py +++ b/src/backend/InvenTree/InvenTree/setting/ldap.py @@ -68,24 +68,22 @@ def get_ldap_config(debug: bool = False) -> dict: str, ) - group_search_dn = get_setting( - 'INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search' - ) + group_search_dn = get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search') find_group_perms = get_boolean_setting( 'INVENTREE_LDAP_FIND_GROUP_PERMS', 'ldap.find_group_perms', True ) - # If group search DN is not configured, group-based features cannot - # work. Disable them gracefully with a warning instead of letting - # django-auth-ldap crash at runtime with a TypeError (see #12225). - if group_search_dn is None: - if find_group_perms or get_setting( - 'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups' - ) or get_setting( - 'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group' - ) or get_setting( - 'INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group' + # If group search DN is not configured (None or empty string), + # group-based features cannot work. Disable them gracefully with a + # warning instead of letting django-auth-ldap crash at runtime with + # a TypeError (see #12225). + if not group_search_dn: + if ( + find_group_perms + or get_setting('INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups') + or get_setting('INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group') + or get_setting('INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group') ): print( '[LDAP] ldap.group_search is not configured; ' @@ -135,13 +133,15 @@ def get_ldap_config(debug: bool = False) -> dict: ), 'AUTH_LDAP_MIRROR_GROUPS': get_boolean_setting( 'INVENTREE_LDAP_MIRROR_GROUPS', 'ldap.mirror_groups', False - ) if group_search_dn is not None else False, + ) + if group_search_dn + else False, 'AUTH_LDAP_GROUP_OBJECT_CLASS': group_object_class, 'AUTH_LDAP_GROUP_SEARCH': django_auth_ldap.config.LDAPSearch( - group_search_dn, - ldap.SCOPE_SUBTREE, - f'(objectClass={group_object_class})', - ) if group_search_dn is not None else None, + group_search_dn, ldap.SCOPE_SUBTREE, f'(objectClass={group_object_class})' + ) + if group_search_dn + else None, 'AUTH_LDAP_GROUP_TYPE_CLASS': group_type_class, 'AUTH_LDAP_GROUP_TYPE_CLASS_ARGS': [*group_type_class_args], 'AUTH_LDAP_GROUP_TYPE_CLASS_KWARGS': {**group_type_class_kwargs}, @@ -150,16 +150,22 @@ def get_ldap_config(debug: bool = False) -> dict: ), 'AUTH_LDAP_REQUIRE_GROUP': get_setting( 'INVENTREE_LDAP_REQUIRE_GROUP', 'ldap.require_group' - ) if group_search_dn is not None else None, + ) + if group_search_dn + else None, 'AUTH_LDAP_DENY_GROUP': get_setting( 'INVENTREE_LDAP_DENY_GROUP', 'ldap.deny_group' - ) if group_search_dn is not None else None, + ) + if group_search_dn + else None, 'AUTH_LDAP_USER_FLAGS_BY_GROUP': get_setting( 'INVENTREE_LDAP_USER_FLAGS_BY_GROUP', 'ldap.user_flags_by_group', default_value=None, typecast=dict, - ) if group_search_dn is not None else None, + ) + if group_search_dn + else None, 'AUTH_LDAP_FIND_GROUP_PERMS': find_group_perms, } diff --git a/src/backend/InvenTree/setting/tests/test_ldap_config.py b/src/backend/InvenTree/setting/tests/test_ldap_config.py index 9f5bb6b8409c..72ea8cbfd289 100644 --- a/src/backend/InvenTree/setting/tests/test_ldap_config.py +++ b/src/backend/InvenTree/setting/tests/test_ldap_config.py @@ -17,6 +17,7 @@ def ldap_module(): # so we can import it directly without those packages installed # as long as we mock them for the get_ldap_config call. import InvenTree.InvenTree.setting.ldap as ldap_mod + importlib.reload(ldap_mod) return ldap_mod @@ -26,9 +27,10 @@ class TestLdapGroupSearchMissing: def test_find_group_perms_disabled_when_group_search_missing(self, ldap_module): """When group_search is None, find_group_perms must be False.""" - with mock.patch.object(ldap_module, 'get_setting') as gs, \ - mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: - + with ( + mock.patch.object(ldap_module, 'get_setting') as gs, + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs, + ): # get_setting: return None for group_search, defaults for others def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GROUP_SEARCH' in env_key: @@ -36,10 +38,14 @@ def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GLOBAL_OPTIONS' in env_key: return kwargs.get('default_value', {}) or {} if 'GROUP_TYPE_CLASS_ARGS' in env_key: - return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + return ( + kwargs.get('default_value', []) + if 'default_value' in kwargs + else [] + ) if 'GROUP_TYPE_CLASS_KWARGS' in env_key: return kwargs.get('default_value', {}) or {} - return kwargs.get('default_value', None) + return kwargs.get('default_value') gs.side_effect = gs_side_effect @@ -69,10 +75,62 @@ def gbs_side_effect(env_key, yaml_key, default): assert config['AUTH_LDAP_DENY_GROUP'] is None assert config['AUTH_LDAP_USER_FLAGS_BY_GROUP'] is None + def test_find_group_perms_disabled_when_group_search_empty(self, ldap_module): + """When group_search is an empty string, find_group_perms must be False.""" + with ( + mock.patch.object(ldap_module, 'get_setting') as gs, + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs, + ): + # get_setting: return "" for group_search, defaults for others + def gs_side_effect(env_key, yaml_key, *args, **kwargs): + if 'GROUP_SEARCH' in env_key: + return '' + if 'GLOBAL_OPTIONS' in env_key: + return kwargs.get('default_value', {}) or {} + if 'GROUP_TYPE_CLASS_ARGS' in env_key: + return ( + kwargs.get('default_value', []) + if 'default_value' in kwargs + else [] + ) + if 'GROUP_TYPE_CLASS_KWARGS' in env_key: + return kwargs.get('default_value', {}) or {} + return kwargs.get('default_value') + + gs.side_effect = gs_side_effect + + # get_boolean_setting: return True for find_group_perms, False for others + def bbs_side_effect(env_key, yaml_key, default): + if 'FIND_GROUP_PERMS' in env_key: + return True + return default + + gbs.side_effect = bbs_side_effect + + # Mock the imports inside get_ldap_config + with mock.patch('django_auth_ldap.config') as mock_config: + mock_config.LDAPSearch.return_value = mock.MagicMock() + mock_config.GroupOfUniqueNamesType.return_value = mock.MagicMock() + + with mock.patch('ldap') as mock_ldap: + mock_ldap.SCOPE_SUBTREE = 2 + mock_ldap.OPT_REFERRALS = 0 + + config = ldap_module.get_ldap_config(debug=False) + + assert config['AUTH_LDAP_FIND_GROUP_PERMS'] is False + assert config['AUTH_LDAP_GROUP_SEARCH'] is None + assert config['AUTH_LDAP_MIRROR_GROUPS'] is False + assert config['AUTH_LDAP_REQUIRE_GROUP'] is None + assert config['AUTH_LDAP_DENY_GROUP'] is None + assert config['AUTH_LDAP_USER_FLAGS_BY_GROUP'] is None + def test_find_group_perms_enabled_when_group_search_set(self, ldap_module): """When group_search is configured, find_group_perms stays True.""" - with mock.patch.object(ldap_module, 'get_setting') as gs, \ - mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: + with ( + mock.patch.object(ldap_module, 'get_setting') as gs, + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs, + ): def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GROUP_SEARCH' in env_key: @@ -80,10 +138,14 @@ def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GLOBAL_OPTIONS' in env_key: return kwargs.get('default_value', {}) or {} if 'GROUP_TYPE_CLASS_ARGS' in env_key: - return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + return ( + kwargs.get('default_value', []) + if 'default_value' in kwargs + else [] + ) if 'GROUP_TYPE_CLASS_KWARGS' in env_key: return kwargs.get('default_value', {}) or {} - return kwargs.get('default_value', None) + return kwargs.get('default_value') gs.side_effect = gs_side_effect @@ -108,8 +170,10 @@ def gbs_side_effect(env_key, yaml_key, default): def test_find_group_perms_can_be_disabled_explicitly(self, ldap_module): """User can set find_group_perms=False even when group_search is set.""" - with mock.patch.object(ldap_module, 'get_setting') as gs, \ - mock.patch.object(ldap_module, 'get_boolean_setting') as gbs: + with ( + mock.patch.object(ldap_module, 'get_setting') as gs, + mock.patch.object(ldap_module, 'get_boolean_setting') as gbs, + ): def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GROUP_SEARCH' in env_key: @@ -117,10 +181,14 @@ def gs_side_effect(env_key, yaml_key, *args, **kwargs): if 'GLOBAL_OPTIONS' in env_key: return kwargs.get('default_value', {}) or {} if 'GROUP_TYPE_CLASS_ARGS' in env_key: - return kwargs.get('default_value', []) if 'default_value' in kwargs else [] + return ( + kwargs.get('default_value', []) + if 'default_value' in kwargs + else [] + ) if 'GROUP_TYPE_CLASS_KWARGS' in env_key: return kwargs.get('default_value', {}) or {} - return kwargs.get('default_value', None) + return kwargs.get('default_value') gs.side_effect = gs_side_effect @@ -142,4 +210,4 @@ def gbs_side_effect(env_key, yaml_key, default): assert config['AUTH_LDAP_FIND_GROUP_PERMS'] is False # Group search is still configured (user might use it for other features) - assert config['AUTH_LDAP_GROUP_SEARCH'] is not None \ No newline at end of file + assert config['AUTH_LDAP_GROUP_SEARCH'] is not None From 7259a82b88a08c75081ed7e6e5a72b2861d40b66 Mon Sep 17 00:00:00 2001 From: AndrianBalanescu <37745667+AndrianBalanescu@users.noreply.github.com> Date: Sat, 15 Aug 2026 14:08:38 -0500 Subject: [PATCH 3/3] test: fix LDAP module import path --- src/backend/InvenTree/setting/tests/test_ldap_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/InvenTree/setting/tests/test_ldap_config.py b/src/backend/InvenTree/setting/tests/test_ldap_config.py index 72ea8cbfd289..e3983fa70ced 100644 --- a/src/backend/InvenTree/setting/tests/test_ldap_config.py +++ b/src/backend/InvenTree/setting/tests/test_ldap_config.py @@ -16,7 +16,7 @@ def ldap_module(): # The module imports django_auth_ldap and ldap at function level, # so we can import it directly without those packages installed # as long as we mock them for the get_ldap_config call. - import InvenTree.InvenTree.setting.ldap as ldap_mod + import InvenTree.setting.ldap as ldap_mod importlib.reload(ldap_mod) return ldap_mod