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 6c71ffd6..d40e59e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,14 +7,14 @@ 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 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' @@ -25,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 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 diff --git a/django_auth_ldap/backend.py b/django_auth_ldap/backend.py index c63b6cec..513810f8 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 @@ -643,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 @@ -777,8 +776,12 @@ 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()) + target_group_names = frozenset( + filter(None, self._get_groups().get_group_names()) + ) except ldap.LDAPError as e: _report_error( type(self.backend), @@ -833,6 +836,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/pyproject.toml b/pyproject.toml index 3b4a52b4..b026a43b 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 = [ @@ -15,15 +15,14 @@ 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", "Intended Audience :: System Administrators", "License :: OSI Approved :: BSD License", "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/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/tests/tests.py b/tests/tests.py index 41cb3735..9c148966 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", @@ -1343,8 +1343,32 @@ def test_group_mirroring(self): alice = authenticate(username="alice", password="password") - self.assertEqual(Group.objects.count(), 3) - self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) + groups = set(Group.objects.all()) + self.assertEqual( + {g.name for g in groups}, + {"active_px", "staff_px", "superuser_px"}, + ) + self.assertEqual(set(alice.groups.all()), groups) + + 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") + groups = set(Group.objects.all()) + self.assertEqual( + {g.name for g in groups}, + {"active_px", "staff_px"}, + ) + self.assertEqual(set(alice.groups.all()), groups) def test_nested_group_mirroring(self): self._init_settings( @@ -1821,3 +1845,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 diff --git a/tox.ini b/tox.ini index 87ffa51a..20a5fe84 100644 --- a/tox.ini +++ b/tox.ini @@ -3,26 +3,27 @@ envlist = ruff docs django42 - django50 django51 + django52 djangomain 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} - 3.12 = django{42,50,51,main} - 3.13 = django{42,50,51,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 = {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 django51: Django>=5.1b1,<5.2 + django52: Django>=5.2,<6.0 djangomain: https://github.com/django/django/archive/main.tar.gz [testenv:ruff]
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: