From 1966c9ee012f9ec42f1934201cdb729c0d5f183c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Thu, 10 Oct 2024 10:36:15 +0200 Subject: [PATCH 01/12] Drop support for EOL Python 3.8 --- .github/workflows/test.yml | 1 - pyproject.toml | 3 +-- tox.ini | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c71ffd6..046b73bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,6 @@ jobs: matrix: # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django python-version: - - '3.8' - '3.9' - '3.10' - '3.11' diff --git a/pyproject.toml b/pyproject.toml index 3b4a52b4..8bb75a0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "django-auth-ldap" -requires-python = ">=3.8" +requires-python = ">=3.9" description = "Django LDAP authentication backend" readme = "README.rst" authors = [ @@ -23,7 +23,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", diff --git a/tox.ini b/tox.ini index 87ffa51a..121f6e55 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,6 @@ isolated_build = true [gh] python = - 3.8 = django42 3.9 = django42 3.10 = django{42,50,51,main} 3.11 = django{42,50,51,main} From 943c8002bc51532e99f482ed10301b3bd4938865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Thu, 10 Oct 2024 10:30:56 +0200 Subject: [PATCH 02/12] Allow importing the backend without loading apps Make it possible to monkey-patch the backend from the settings. --- django_auth_ldap/backend.py | 5 ++++- tests/import_test_without_django.py | 16 ++++++++++++++++ tox.ini | 4 +++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/import_test_without_django.py diff --git a/django_auth_ldap/backend.py b/django_auth_ldap/backend.py index c63b6cec..4356d79c 100644 --- a/django_auth_ldap/backend.py +++ b/django_auth_ldap/backend.py @@ -51,7 +51,6 @@ import django.dispatch import ldap from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group, Permission from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist @@ -777,6 +776,8 @@ def _mirror_groups(self): Mirrors the user's LDAP groups in the Django database and updates the user's membership. """ + from django.contrib.auth.models import Group + try: target_group_names = frozenset(self._get_groups().get_group_names()) except ldap.LDAPError as e: @@ -833,6 +834,8 @@ def _load_group_permissions(self): Populates self._group_permissions based on LDAP group membership and Django group permissions. """ + from django.contrib.auth.models import Permission + group_names = self._get_groups().get_group_names() perms = Permission.objects.filter(group__name__in=group_names) diff --git a/tests/import_test_without_django.py b/tests/import_test_without_django.py new file mode 100644 index 00000000..0fbdc9b4 --- /dev/null +++ b/tests/import_test_without_django.py @@ -0,0 +1,16 @@ +import os +from unittest import TestCase + + +class TestLoading(TestCase): + def test_django_not_ready(self): + orig_env = os.environ.copy() + + def reset_env(): + os.environ = orig_env + + self.addCleanup(reset_env) + + os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" + + import django_auth_ldap.backend # noqa: F401 diff --git a/tox.ini b/tox.ini index 121f6e55..dd4f3f91 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,9 @@ python = 3.13 = django{42,50,51,main} [testenv] -commands = {envpython} -Wa -b -m django test --settings tests.settings +commands = + {envpython} -Wa -b -m django test --settings tests.settings + {envpython} -Wa -b -m unittest discover --pattern *_test_without_django.py deps = django42: Django>=4.2,<4.3 django50: Django>=5.0,<5.1 From 16ea6fd4158bc47549f59ed7fce7d2ad0472a80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Sun, 9 Feb 2025 14:17:26 +0100 Subject: [PATCH 03/12] Stop testing Python 3.10 and 3.11 against django main Django dropped support for these Python versions on main. --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index dd4f3f91..f635c98c 100644 --- a/tox.ini +++ b/tox.ini @@ -11,8 +11,8 @@ isolated_build = true [gh] python = 3.9 = django42 - 3.10 = django{42,50,51,main} - 3.11 = django{42,50,51,main} + 3.10 = django{42,50,51} + 3.11 = django{42,50,51} 3.12 = django{42,50,51,main} 3.13 = django{42,50,51,main} From c5c768388af2ab2a4266d9de94d8ec8ae3a54975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 7 May 2025 10:25:16 +0200 Subject: [PATCH 04/12] Align CI with python-ldap Ubuntu 24.04 does not support the AppArmor trick used on 22.04, and there is no documented workaround. Follow the configuration from python-ldap. --- .github/workflows/docs.yml | 11 +++++------ .github/workflows/test.yml | 9 ++++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d47a1308..efef6f75 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,7 +7,8 @@ on: jobs: build: name: Documentation - runs-on: ubuntu-latest + # https://github.com/python-ldap/python-ldap/blob/main/.github/workflows/ci.yml + runs-on: ubuntu-22.04 env: TOXENV: docs @@ -15,11 +16,9 @@ jobs: steps: - name: Install LDAP libs run: | - sudo apt-get update - # https://www.python-ldap.org/en/latest/installing.html#debian - sudo apt-get install slapd ldap-utils libldap2-dev libsasl2-dev - # https://github.com/python-ldap/python-ldap/issues/370 - sudo apt-get install apparmor-utils + sudo apt update + # https://github.com/python-ldap/python-ldap/blob/main/.github/workflows/ci.yml + sudo apt install -y ldap-utils slapd enchant-2 libldap2-dev libsasl2-dev apparmor-utils sudo aa-disable /usr/sbin/slapd - uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 046b73bb..d40e59e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,8 @@ on: jobs: test: name: Python ${{ matrix.python-version }} / ${{ matrix.tox-environment }} - runs-on: ubuntu-latest + # https://github.com/python-ldap/python-ldap/blob/main/.github/workflows/ci.yml + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -24,10 +25,8 @@ jobs: - name: Install LDAP libs run: | sudo apt-get update - # https://www.python-ldap.org/en/latest/installing.html#debian - sudo apt-get install slapd ldap-utils libldap2-dev libsasl2-dev - # https://github.com/python-ldap/python-ldap/issues/370 - sudo apt-get install apparmor-utils + # https://github.com/python-ldap/python-ldap/blob/main/.github/workflows/ci.yml + sudo apt install -y ldap-utils slapd enchant-2 libldap2-dev libsasl2-dev apparmor-utils sudo aa-disable /usr/sbin/slapd - uses: actions/checkout@v4 From 31c4f515a3c8fc2492c67d90b207689c8a67ea42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 7 May 2025 10:20:23 +0200 Subject: [PATCH 05/12] Add support for Django 5.2 --- pyproject.toml | 1 + tox.ini | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8bb75a0e..cb590cdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ classifiers = [ "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", + "Framework :: Django :: 5.2", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: BSD License", diff --git a/tox.ini b/tox.ini index f635c98c..be608a8e 100644 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,17 @@ envlist = django42 django50 django51 + django52 djangomain isolated_build = true [gh] python = 3.9 = django42 - 3.10 = django{42,50,51} - 3.11 = django{42,50,51} - 3.12 = django{42,50,51,main} - 3.13 = django{42,50,51,main} + 3.10 = django{42,50,51,52} + 3.11 = django{42,50,51,52} + 3.12 = django{42,50,51,52,main} + 3.13 = django{42,50,51,52,main} [testenv] commands = @@ -24,6 +25,7 @@ deps = django42: Django>=4.2,<4.3 django50: Django>=5.0,<5.1 django51: Django>=5.1b1,<5.2 + django52: Django>=5.2,<6.0 djangomain: https://github.com/django/django/archive/main.tar.gz [testenv:ruff] From 1400829cadb60212333283eb7cd6ee7dfd3d2f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 7 May 2025 10:46:41 +0200 Subject: [PATCH 06/12] Drop support for Django 5.0 --- pyproject.toml | 1 - tox.ini | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cb590cdd..b026a43b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ classifiers = [ "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 4.2", - "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", "Intended Audience :: Developers", diff --git a/tox.ini b/tox.ini index be608a8e..20a5fe84 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,6 @@ envlist = ruff docs django42 - django50 django51 django52 djangomain @@ -12,10 +11,10 @@ isolated_build = true [gh] python = 3.9 = django42 - 3.10 = django{42,50,51,52} - 3.11 = django{42,50,51,52} - 3.12 = django{42,50,51,52,main} - 3.13 = django{42,50,51,52,main} + 3.10 = django{42,51,52} + 3.11 = django{42,51,52} + 3.12 = django{42,51,52,main} + 3.13 = django{42,51,52,main} [testenv] commands = @@ -23,7 +22,6 @@ commands = {envpython} -Wa -b -m unittest discover --pattern *_test_without_django.py deps = django42: Django>=4.2,<4.3 - django50: Django>=5.0,<5.1 django51: Django>=5.1b1,<5.2 django52: Django>=5.2,<6.0 djangomain: https://github.com/django/django/archive/main.tar.gz From f77cdec71433ed00551d51341348c108c02f14df Mon Sep 17 00:00:00 2001 From: Augustin FL Date: Mon, 5 May 2025 11:11:23 +0200 Subject: [PATCH 07/12] Increase log level to info when creating/populating a user This way, admins have a way to log users creation and update while not logging LDAP traffic. --- django_auth_ldap/backend.py | 4 ++-- tests/tests.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django_auth_ldap/backend.py b/django_auth_ldap/backend.py index 4356d79c..b7159eef 100644 --- a/django_auth_ldap/backend.py +++ b/django_auth_ldap/backend.py @@ -642,12 +642,12 @@ def _get_or_create_user(self, force_populate=False): "user does not satisfy AUTH_LDAP_NO_NEW_USERS" ) - logger.debug("Creating Django user %s", username) + logger.info("Creating Django user %s", username) self._user.set_unusable_password() save_user = True if should_populate: - logger.debug("Populating Django user %s", username) + logger.info("Populating Django user %s", username) self._populate_user() save_user = True diff --git a/tests/tests.py b/tests/tests.py index 41cb3735..bf21179a 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -582,8 +582,8 @@ def test_populate_user_with_missing_attribute(self): [(log.levelname, log.msg, log.args) for log in logs.records], [ ("DEBUG", "Binding as %s", (dn,)), - ("DEBUG", "Creating Django user %s", ("alice",)), - ("DEBUG", "Populating Django user %s", ("alice",)), + ("INFO", "Creating Django user %s", ("alice",)), + ("INFO", "Populating Django user %s", ("alice",)), ("DEBUG", "Binding as %s", ("",)), ( "DEBUG", From ceb59a85502625bbc668241ad819f105bc24c3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 7 May 2025 10:57:39 +0200 Subject: [PATCH 08/12] Fix documentation build on readthedocs https://about.readthedocs.com/blog/2024/12/deprecate-config-files-without-sphinx-or-mkdocs-config/ --- .readthedocs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 2646802c..0c6dba53 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,3 +7,5 @@ build: python: install: - requirements: docs/requirements.txt +sphinx: + configuration: docs/conf.py From c401d6bd726ccc4652a897cbd51c914ded89c1f0 Mon Sep 17 00:00:00 2001 From: Piotr Kubiak Date: Mon, 10 Mar 2025 20:22:41 +0100 Subject: [PATCH 09/12] Filter empty Django group names when mirroring groups --- django_auth_ldap/backend.py | 4 +++- tests/tests.py | 40 ++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/django_auth_ldap/backend.py b/django_auth_ldap/backend.py index b7159eef..513810f8 100644 --- a/django_auth_ldap/backend.py +++ b/django_auth_ldap/backend.py @@ -779,7 +779,9 @@ def _mirror_groups(self): from django.contrib.auth.models import Group try: - target_group_names = frozenset(self._get_groups().get_group_names()) + target_group_names = frozenset( + filter(None, self._get_groups().get_group_names()) + ) except ldap.LDAPError as e: _report_error( type(self.backend), diff --git a/tests/tests.py b/tests/tests.py index bf21179a..c642ec4e 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1343,7 +1343,36 @@ def test_group_mirroring(self): alice = authenticate(username="alice", password="password") - self.assertEqual(Group.objects.count(), 3) + self.assertEqual( + set(Group.objects.all().values_list("name", flat=True)), + { + "active_px", + "staff_px", + "superuser_px", + }, + ) + self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) + + def test_group_mirroring_custom_grouptype(self): + self._init_settings( + USER_DN_TEMPLATE="uid=%(user)s,ou=people,o=test", + GROUP_SEARCH=LDAPSearch( + "ou=groups,o=test", ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)" + ), + GROUP_TYPE=CustomGroupType(), + MIRROR_GROUPS=True, + ) + + self.assertEqual(Group.objects.count(), 0) + + alice = authenticate(username="alice", password="password") + self.assertEqual( + set(Group.objects.all().values_list("name", flat=True)), + { + "active_px", + "staff_px", + }, + ) self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) def test_nested_group_mirroring(self): @@ -1821,3 +1850,12 @@ def _init_groups(self): active_nis = Group.objects.create(name="active_nis") active_nis.permissions.add(*permissions) + + +class CustomGroupType(PosixGroupType): + def group_name_from_info(self, group_info): + name = super().group_name_from_info(group_info) + if name.startswith("superuser"): + name = None + + return name From 53adb73ef39151e3d91fb8aab9e0824e9d0a63cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Wed, 7 May 2025 10:17:34 +0200 Subject: [PATCH 10/12] fixup! Filter empty Django group names when mirroring groups --- tests/tests.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index c642ec4e..9c148966 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1343,15 +1343,12 @@ def test_group_mirroring(self): alice = authenticate(username="alice", password="password") + groups = set(Group.objects.all()) self.assertEqual( - set(Group.objects.all().values_list("name", flat=True)), - { - "active_px", - "staff_px", - "superuser_px", - }, + {g.name for g in groups}, + {"active_px", "staff_px", "superuser_px"}, ) - self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) + self.assertEqual(set(alice.groups.all()), groups) def test_group_mirroring_custom_grouptype(self): self._init_settings( @@ -1366,14 +1363,12 @@ def test_group_mirroring_custom_grouptype(self): self.assertEqual(Group.objects.count(), 0) alice = authenticate(username="alice", password="password") + groups = set(Group.objects.all()) self.assertEqual( - set(Group.objects.all().values_list("name", flat=True)), - { - "active_px", - "staff_px", - }, + {g.name for g in groups}, + {"active_px", "staff_px"}, ) - self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) + self.assertEqual(set(alice.groups.all()), groups) def test_nested_group_mirroring(self): self._init_settings( From 6a1635306d9a601a892b904a075e180288654d8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:57:16 +0000 Subject: [PATCH 11/12] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/check.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 80dca6f4..48e0ec4e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -9,7 +9,7 @@ jobs: name: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index efef6f75..bde86958 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: sudo apt install -y ldap-utils slapd enchant-2 libldap2-dev libsasl2-dev apparmor-utils sudo aa-disable /usr/sbin/slapd - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cbc194f..5a84e704 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ jobs: name: Build distribution files runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d40e59e6..b7093e45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: sudo apt install -y ldap-utils slapd enchant-2 libldap2-dev libsasl2-dev apparmor-utils sudo aa-disable /usr/sbin/slapd - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From 485695aa3d883f451748092f15cf2df1d261d922 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 01:58:07 +0000 Subject: [PATCH 12/12] Bump actions/download-artifact from 4 to 5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a84e704..42c93576 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: id-token: write steps: - name: Retrieve artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist path: dist/