From 45d22edaffd36da9192e400061bcb89796a764d5 Mon Sep 17 00:00:00 2001 From: mmmduft Date: Thu, 11 Jan 2024 21:56:26 +1100 Subject: [PATCH 01/10] Add three additional configuration options Adds support for additional configuration options. LDAP_AUTH_TLS_CA_CERTS_FILE LDAP_AUTH_TLS_VALIDATE_CERT LDAP_AUTH_TLS_CIPHERS LDAP_AUTH_ATTRIBUTES It may be wise to set the default of LDAP_AUTH_TLS_VALIDATE_CERT to ssl.CERT_REQUIRED in the future, however this would be a breaking change. --- README.rst | 18 ++++++++++++++ django_python3_ldap/conf.py | 26 ++++++++++++++++--- django_python3_ldap/ldap.py | 48 +++++++++++++++++++++++++----------- django_python3_ldap/tests.py | 39 ++++++++++++++++++++++------- 4 files changed, 105 insertions(+), 26 deletions(-) diff --git a/README.rst b/README.rst index 5b5e334..fea6fea 100644 --- a/README.rst +++ b/README.rst @@ -41,6 +41,20 @@ Available settings import ssl LDAP_AUTH_TLS_VERSION = ssl.PROTOCOL_TLSv1_2 + # Specifies if the server certificate must be validated, values can be: CERT_NONE (certificates are ignored), + # CERT_OPTIONAL (not required, but validated if provided) and CERT_REQUIRED (required and validated). The default + # CERT_OPTIONAL is not secure and it is recommended that CERT_REQUIRED is used. + LDAP_AUTH_TLS_VALIDATE_CERT = ssl.CERT_OPTIONAL + + # A string must be the path to a file in PEM format containing the server, and any number of CA certificates needed + # to establish the server certificate’s authenticity. + = None + + # String in the OpenSSL cipher list format specifying which ciphers must be used. See https://ldap3.readthedocs.io/en/latest/ssltls.html . It + # works on recent Python interpreters that allow to change the cipher in the SSLContext or in the the wrap_socket() + #method, it’s ignored on older versions. + LDAP_AUTH_TLS_CIPHERS = None + # The LDAP search base for looking up users. LDAP_AUTH_SEARCH_BASE = "ou=people,dc=example,dc=com" @@ -56,6 +70,7 @@ Available settings "email": "mail", } + # A tuple of django model fields used to uniquely identify a user. LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",) @@ -81,6 +96,9 @@ Available settings # Use this to support different types of LDAP server. LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_openldap" + # A single attribute or a list of attributes to be returned by LDAP operations. + LDAP_AUTH_ATTRIBUTES = ldap3.ALL_ATTRIBUTES + # Sets the login domain for Active Directory users. LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = None diff --git a/django_python3_ldap/conf.py b/django_python3_ldap/conf.py index f598759..64e97bb 100644 --- a/django_python3_ldap/conf.py +++ b/django_python3_ldap/conf.py @@ -1,8 +1,8 @@ """ Settings used by django-python3. """ -from ssl import PROTOCOL_TLS - +import ldap3 +import ssl from django.conf import settings @@ -46,7 +46,22 @@ def __init__(self, settings): LDAP_AUTH_TLS_VERSION = LazySetting( name="LDAP_AUTH_TLS_VERSION", - default=PROTOCOL_TLS, + default=None, + ) + + LDAP_AUTH_TLS_VALIDATE_CERT = LazySetting( + name="LDAP_AUTH_TLS_VALIDATE_CERT", + default=None, + ) + + LDAP_AUTH_TLS_CA_CERTS_FILE = LazySetting( + name="LDAP_AUTH_TLS_CA_CERTS_FILE", + default=None, + ) + + LDAP_AUTH_TLS_CIPHERS = LazySetting( + name="LDAP_AUTH_TLS_CIPHERS", + default=None, ) LDAP_AUTH_SEARCH_BASE = LazySetting( @@ -96,6 +111,11 @@ def __init__(self, settings): default="django_python3_ldap.utils.format_username_openldap", ) + LDAP_AUTH_ATTRIBUTES = LazySetting( + name="LDAP_AUTH_ATTRIBUTES", + default=ldap3.ALL_ATTRIBUTES, + ) + LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = LazySetting( name="LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN", default=None, diff --git a/django_python3_ldap/ldap.py b/django_python3_ldap/ldap.py index 922e0cd..35eae7d 100644 --- a/django_python3_ldap/ldap.py +++ b/django_python3_ldap/ldap.py @@ -1,6 +1,7 @@ """ Low-level LDAP hooks. """ +import ssl import ldap3 from ldap3.core.exceptions import LDAPException @@ -97,7 +98,7 @@ def iter_users(self): search_base=settings.LDAP_AUTH_SEARCH_BASE, search_filter=format_search_filter({}), search_scope=ldap3.SUBTREE, - attributes=ldap3.ALL_ATTRIBUTES, + attributes=settings.LDAP_AUTH_ATTRIBUTES, get_operational_attributes=True, paged_size=30, ) @@ -133,13 +134,38 @@ def has_user(self, **kwargs): search_base=settings.LDAP_AUTH_SEARCH_BASE, search_filter=format_search_filter(kwargs), search_scope=ldap3.SUBTREE, - attributes=ldap3.ALL_ATTRIBUTES, + attributes=settings.LDAP_AUTH_ATTRIBUTES, get_operational_attributes=True, size_limit=1, ) return bool(len(self._connection.response) > 0 and self._connection.response[0].get("attributes")) +def get_tls_options(settings): + tls_options = {} + + if not settings.LDAP_AUTH_USE_TLS: + return None + + tls_options['validate'] = settings.LDAP_AUTH_TLS_VALIDATE_CERT + + if tls_options['validate'] != ssl.CERT_REQUIRED: + logger.info( + "LDAP_AUTH_VALIDATE_CERT is set to not ssl.CERT_REQUIRED, certificate validation may not be enforced. This configuration is considered insecure.") + + if settings.LDAP_AUTH_TLS_CA_CERTS_FILE: + tls_options['ca_certs_file'] = settings.LDAP_AUTH_TLS_CA_CERTS_FILE + + if settings.LDAP_AUTH_TLS_VERSION: + tls_options['version'] = settings.LDAP_AUTH_TLS_VERSION + + if settings.LDAP_AUTH_TLS_CIPHERS: + tls_options['ciphers'] = settings.LDAP_AUTH_TLS_CIPHERS + + return (ldap3.Tls(**tls_options)) + + + @contextmanager def connection(**kwargs): """ @@ -167,25 +193,19 @@ def connection(**kwargs): if not isinstance(auth_url, list): auth_url = [auth_url] for u in auth_url: - # Include SSL / TLS, if requested. - server_args = { - "allowed_referral_hosts": [("*", True)], - "get_info": ldap3.NONE, - "connect_timeout": settings.LDAP_AUTH_CONNECT_TIMEOUT, - } - if settings.LDAP_AUTH_USE_TLS: - server_args["tls"] = ldap3.Tls( - ciphers="ALL", - version=settings.LDAP_AUTH_TLS_VERSION, - ) server_pool.add( ldap3.Server( u, - **server_args, + allowed_referral_hosts=[("*", True)], + get_info=ldap3.NONE, + connect_timeout=settings.LDAP_AUTH_CONNECT_TIMEOUT, + tls=get_tls_options(settings), + use_ssl=settings.LDAP_AUTH_USE_TLS ) ) # Connect. try: + # Include SSL / TLS, if requested. connection_args = { "user": username, "password": password, diff --git a/django_python3_ldap/tests.py b/django_python3_ldap/tests.py index dc7cbe6..979d8a7 100644 --- a/django_python3_ldap/tests.py +++ b/django_python3_ldap/tests.py @@ -1,6 +1,7 @@ # encoding=utf-8 from __future__ import unicode_literals +import ssl from unittest import skipUnless, skip from io import StringIO @@ -116,15 +117,6 @@ def testRepeatedUserAuthenticationDoestRecreateUsers(self): # Ensure that the user isn't recreated on second access. self.assertEqual(user_1.pk, user_2.pk) - @skip("FIXME: test server currently uses outdated TLS cyphers") - def testAuthenticateWithTLS(self): - with self.settings(LDAP_AUTH_USE_TLS=True): - user = authenticate( - username=settings.LDAP_AUTH_TEST_USER_USERNAME, - password=settings.LDAP_AUTH_TEST_USER_PASSWORD, - ) - self.assertIsInstance(user, User) - self.assertEqual(user.username, settings.LDAP_AUTH_TEST_USER_USERNAME) def testAuthenticateWithRebind(self): with self.settings( @@ -412,3 +404,32 @@ def testReCleanUsersDoesntRecreateUsers(self): call_command("ldap_clean_users", verbosity=0, purge=True) user_count_2 = User.objects.count() self.assertEqual(user_count_1, user_count_2) + + +@skipUnless(settings.LDAP_AUTH_TEST_USER_USERNAME, "No settings.LDAP_AUTH_TEST_USER_USERNAME supplied.") +@skipUnless(settings.LDAP_AUTH_TEST_USER_PASSWORD, "No settings.LDAP_AUTH_TEST_USER_PASSWORD supplied.") +@skipUnless(settings.LDAP_AUTH_USER_LOOKUP_FIELDS == ("username",), "Cannot test using custom lookup fields.") +@skipUnless(django_settings.AUTH_USER_MODEL == "auth.User", "Cannot test using a custom user model.") +class TestSSL(TestCase): + + def setUp(self): + super(TestSSL, self).setUp() + User.objects.all().delete() + + def testAuthenticateWithTLS(self): + with self.settings(LDAP_AUTH_USE_TLS=True, LDAP_AUTH_TLS_VALIDATE_CERT=ssl.CERT_NONE): + user = authenticate( + username=settings.LDAP_AUTH_TEST_USER_USERNAME, + password=settings.LDAP_AUTH_TEST_USER_PASSWORD, + ) + self.assertIsInstance(user, User) + self.assertEqual(user.username, settings.LDAP_AUTH_TEST_USER_USERNAME) + + # This should fail as server is presenting a self-signed certificate. + def test_validate_required_self_signed(self): + with self.settings(LDAP_AUTH_USE_TLS=True, LDAP_AUTH_TLS_VALIDATE_CERT=ssl.CERT_REQUIRED): + with self.assertRaises(Exception): + authenticate( + username='any', + password='any', + ) From 74075aeb4856ac3583998c4b59a24b5ff88cb24b Mon Sep 17 00:00:00 2001 From: mmmduft Date: Thu, 11 Jan 2024 21:59:13 +1100 Subject: [PATCH 02/10] Update README.rst Fix typo --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index fea6fea..a2ef89b 100644 --- a/README.rst +++ b/README.rst @@ -46,10 +46,6 @@ Available settings # CERT_OPTIONAL is not secure and it is recommended that CERT_REQUIRED is used. LDAP_AUTH_TLS_VALIDATE_CERT = ssl.CERT_OPTIONAL - # A string must be the path to a file in PEM format containing the server, and any number of CA certificates needed - # to establish the server certificate’s authenticity. - = None - # String in the OpenSSL cipher list format specifying which ciphers must be used. See https://ldap3.readthedocs.io/en/latest/ssltls.html . It # works on recent Python interpreters that allow to change the cipher in the SSLContext or in the the wrap_socket() #method, it’s ignored on older versions. From 02aefbacdc36d3ee32b8a9324f46740140150bae Mon Sep 17 00:00:00 2001 From: mmmduft Date: Sun, 14 Jan 2024 10:40:33 +1100 Subject: [PATCH 03/10] Remove unrequired import --- django_python3_ldap/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/django_python3_ldap/conf.py b/django_python3_ldap/conf.py index 64e97bb..5530cbe 100644 --- a/django_python3_ldap/conf.py +++ b/django_python3_ldap/conf.py @@ -2,7 +2,6 @@ Settings used by django-python3. """ import ldap3 -import ssl from django.conf import settings From 923f51f6ab546932161d1af76e184cf7b5f044a8 Mon Sep 17 00:00:00 2001 From: mmmduft Date: Sun, 14 Jan 2024 10:44:53 +1100 Subject: [PATCH 04/10] Handling empty attributes specified in LDAP_AUTH_SYNC_ATTRIBUTES Attributes listed in LDAP_AUTH_SYNC_ATTRIBUTES without values are returned as empty lists. Prevent IndexError --- django_python3_ldap/ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_python3_ldap/ldap.py b/django_python3_ldap/ldap.py index 35eae7d..bb45ff4 100644 --- a/django_python3_ldap/ldap.py +++ b/django_python3_ldap/ldap.py @@ -49,7 +49,7 @@ def _get_or_create_user(self, user_data): user_fields = { field_name: ( attributes[attribute_name][0] - if isinstance(attributes[attribute_name], (list, tuple)) else + if isinstance(attributes[attribute_name], (list, tuple)) and len(attributes[attribute_name]) > 0 else attributes[attribute_name] ) for field_name, attribute_name From 10535db81b1b2febc74ba384593a92216b824be6 Mon Sep 17 00:00:00 2001 From: mmmduft Date: Sun, 14 Jan 2024 10:45:07 +1100 Subject: [PATCH 05/10] Add tests for LDAP_AUTH_SYNC_ATTRIBUTES --- django_python3_ldap/tests.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/django_python3_ldap/tests.py b/django_python3_ldap/tests.py index 979d8a7..13da4d4 100644 --- a/django_python3_ldap/tests.py +++ b/django_python3_ldap/tests.py @@ -406,6 +406,37 @@ def testReCleanUsersDoesntRecreateUsers(self): self.assertEqual(user_count_1, user_count_2) +@skipUnless(settings.LDAP_AUTH_TEST_USER_USERNAME, "No settings.LDAP_AUTH_TEST_USER_USERNAME supplied.") +@skipUnless(settings.LDAP_AUTH_TEST_USER_PASSWORD, "No settings.LDAP_AUTH_TEST_USER_PASSWORD supplied.") +@skipUnless(settings.LDAP_AUTH_USER_LOOKUP_FIELDS == ("username",), "Cannot test using custom lookup fields.") +@skipUnless(django_settings.AUTH_USER_MODEL == "auth.User", "Cannot test using a custom user model.") +class TestAttrib(TestCase): + + def setUp(self): + super(TestAttrib, self).setUp() + User.objects.all().delete() + + def testSyncUsersCreatesUsers(self): + with self.settings( + LDAP_AUTH_ATTRIBUTES=['givenName', "homeDirectory", "uid"], + LDAP_AUTH_USER_FIELDS={ + "username": "uid", + "first_name": "givenName", + "last_name": "homeDirectory", + "email": "mail", + } + + ): + user = authenticate( + username=settings.LDAP_AUTH_TEST_USER_USERNAME, + password=settings.LDAP_AUTH_TEST_USER_PASSWORD, + ) + self.assertIsInstance(user, User) + self.assertEqual(user.last_name, 'home') + self.assertEqual(user.email, '') + + + @skipUnless(settings.LDAP_AUTH_TEST_USER_USERNAME, "No settings.LDAP_AUTH_TEST_USER_USERNAME supplied.") @skipUnless(settings.LDAP_AUTH_TEST_USER_PASSWORD, "No settings.LDAP_AUTH_TEST_USER_PASSWORD supplied.") @skipUnless(settings.LDAP_AUTH_USER_LOOKUP_FIELDS == ("username",), "Cannot test using custom lookup fields.") From b78967220dce184f71bfa7252808ac4935f0959c Mon Sep 17 00:00:00 2001 From: mmmduft Date: Sun, 14 Jan 2024 11:16:25 +1100 Subject: [PATCH 06/10] Update README.rst Add documentation for LDAP_AUTH_TLS_CA_CERTS_FILE --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index a2ef89b..42cf92c 100644 --- a/README.rst +++ b/README.rst @@ -46,6 +46,12 @@ Available settings # CERT_OPTIONAL is not secure and it is recommended that CERT_REQUIRED is used. LDAP_AUTH_TLS_VALIDATE_CERT = ssl.CERT_OPTIONAL + + # LDAP_AUTH_TLS_CA_CERTS_FILE a file containing the certificates of trusted certification authorities. Option is valid + only when using SSLContext (i.e. python 2.7.9+ and python 3.4+). + LDAP_AUTH_TLS_CA_CERTS_FILE = None + + # String in the OpenSSL cipher list format specifying which ciphers must be used. See https://ldap3.readthedocs.io/en/latest/ssltls.html . It # works on recent Python interpreters that allow to change the cipher in the SSLContext or in the the wrap_socket() #method, it’s ignored on older versions. From 4327331d8c34e73e6349a2d74bb61297a66234e9 Mon Sep 17 00:00:00 2001 From: mmmduft Date: Mon, 15 Jan 2024 20:59:16 +1100 Subject: [PATCH 07/10] Change handling of TLS validate . Do set validate=None when LDAP_AUTH_TLS_VALIDATE_CERT is not set. Removed log message, as this may not be right place for configuration checks. --- README.rst | 7 +++---- django_python3_ldap/ldap.py | 7 ++----- django_python3_ldap/tests.py | 8 ++++++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 42cf92c..1c891b9 100644 --- a/README.rst +++ b/README.rst @@ -43,12 +43,11 @@ Available settings # Specifies if the server certificate must be validated, values can be: CERT_NONE (certificates are ignored), # CERT_OPTIONAL (not required, but validated if provided) and CERT_REQUIRED (required and validated). The default - # CERT_OPTIONAL is not secure and it is recommended that CERT_REQUIRED is used. - LDAP_AUTH_TLS_VALIDATE_CERT = ssl.CERT_OPTIONAL + # is not secure and it is recommended that CERT_REQUIRED is used instead. + LDAP_AUTH_TLS_VALIDATE_CERT = None - # LDAP_AUTH_TLS_CA_CERTS_FILE a file containing the certificates of trusted certification authorities. Option is valid - only when using SSLContext (i.e. python 2.7.9+ and python 3.4+). + # LDAP_AUTH_TLS_CA_CERTS_FILE a file containing the certificates of trusted certification authorities. LDAP_AUTH_TLS_CA_CERTS_FILE = None diff --git a/django_python3_ldap/ldap.py b/django_python3_ldap/ldap.py index bb45ff4..3f7c3c2 100644 --- a/django_python3_ldap/ldap.py +++ b/django_python3_ldap/ldap.py @@ -147,11 +147,8 @@ def get_tls_options(settings): if not settings.LDAP_AUTH_USE_TLS: return None - tls_options['validate'] = settings.LDAP_AUTH_TLS_VALIDATE_CERT - - if tls_options['validate'] != ssl.CERT_REQUIRED: - logger.info( - "LDAP_AUTH_VALIDATE_CERT is set to not ssl.CERT_REQUIRED, certificate validation may not be enforced. This configuration is considered insecure.") + if settings.LDAP_AUTH_TLS_VALIDATE_CERT: + tls_options['validate'] = settings.LDAP_AUTH_TLS_VALIDATE_CERT if settings.LDAP_AUTH_TLS_CA_CERTS_FILE: tls_options['ca_certs_file'] = settings.LDAP_AUTH_TLS_CA_CERTS_FILE diff --git a/django_python3_ldap/tests.py b/django_python3_ldap/tests.py index 13da4d4..ccc58f0 100644 --- a/django_python3_ldap/tests.py +++ b/django_python3_ldap/tests.py @@ -464,3 +464,11 @@ def test_validate_required_self_signed(self): username='any', password='any', ) + + def test_validate_not_specified_self_signed(self): + with self.settings(LDAP_AUTH_USE_TLS=True): + with self.assertRaises(Exception): + authenticate( + username='any', + password='any', + ) \ No newline at end of file From c7d204ed0c592d3e262eb043b4c20b24e406832a Mon Sep 17 00:00:00 2001 From: mmmduft Date: Mon, 15 Jan 2024 21:04:47 +1100 Subject: [PATCH 08/10] Update tests.py Remove incorrect test. --- django_python3_ldap/tests.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/django_python3_ldap/tests.py b/django_python3_ldap/tests.py index ccc58f0..13da4d4 100644 --- a/django_python3_ldap/tests.py +++ b/django_python3_ldap/tests.py @@ -464,11 +464,3 @@ def test_validate_required_self_signed(self): username='any', password='any', ) - - def test_validate_not_specified_self_signed(self): - with self.settings(LDAP_AUTH_USE_TLS=True): - with self.assertRaises(Exception): - authenticate( - username='any', - password='any', - ) \ No newline at end of file From 7b18cb3fc7f3ef3294dea8d2195ce736b6ff6c7d Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Mon, 15 Jan 2024 12:03:25 +0000 Subject: [PATCH 09/10] Fixing README formatting --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 1c891b9..8c662bb 100644 --- a/README.rst +++ b/README.rst @@ -46,11 +46,9 @@ Available settings # is not secure and it is recommended that CERT_REQUIRED is used instead. LDAP_AUTH_TLS_VALIDATE_CERT = None - # LDAP_AUTH_TLS_CA_CERTS_FILE a file containing the certificates of trusted certification authorities. LDAP_AUTH_TLS_CA_CERTS_FILE = None - # String in the OpenSSL cipher list format specifying which ciphers must be used. See https://ldap3.readthedocs.io/en/latest/ssltls.html . It # works on recent Python interpreters that allow to change the cipher in the SSLContext or in the the wrap_socket() #method, it’s ignored on older versions. @@ -71,7 +69,6 @@ Available settings "email": "mail", } - # A tuple of django model fields used to uniquely identify a user. LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",) From 23a228e2ba48b5da5e997f78f34809ab5491a69e Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Mon, 15 Jan 2024 12:06:52 +0000 Subject: [PATCH 10/10] Fixing lints --- .gitignore | 1 + django_python3_ldap/ldap.py | 4 +--- django_python3_ldap/tests.py | 8 +++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 1e37f87..6989ea0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ docs/_build *.sqlite3 .coverage *.egg-info +.venv diff --git a/django_python3_ldap/ldap.py b/django_python3_ldap/ldap.py index 3f7c3c2..a8e95b9 100644 --- a/django_python3_ldap/ldap.py +++ b/django_python3_ldap/ldap.py @@ -1,7 +1,6 @@ """ Low-level LDAP hooks. """ -import ssl import ldap3 from ldap3.core.exceptions import LDAPException @@ -148,7 +147,7 @@ def get_tls_options(settings): return None if settings.LDAP_AUTH_TLS_VALIDATE_CERT: - tls_options['validate'] = settings.LDAP_AUTH_TLS_VALIDATE_CERT + tls_options['validate'] = settings.LDAP_AUTH_TLS_VALIDATE_CERT if settings.LDAP_AUTH_TLS_CA_CERTS_FILE: tls_options['ca_certs_file'] = settings.LDAP_AUTH_TLS_CA_CERTS_FILE @@ -162,7 +161,6 @@ def get_tls_options(settings): return (ldap3.Tls(**tls_options)) - @contextmanager def connection(**kwargs): """ diff --git a/django_python3_ldap/tests.py b/django_python3_ldap/tests.py index 13da4d4..57dbe56 100644 --- a/django_python3_ldap/tests.py +++ b/django_python3_ldap/tests.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import ssl -from unittest import skipUnless, skip +from unittest import skipUnless from io import StringIO from django.test import TestCase, override_settings @@ -117,7 +117,6 @@ def testRepeatedUserAuthenticationDoestRecreateUsers(self): # Ensure that the user isn't recreated on second access. self.assertEqual(user_1.pk, user_2.pk) - def testAuthenticateWithRebind(self): with self.settings( LDAP_AUTH_CONNECTION_USERNAME=settings.LDAP_AUTH_TEST_USER_USERNAME, @@ -418,8 +417,8 @@ def setUp(self): def testSyncUsersCreatesUsers(self): with self.settings( - LDAP_AUTH_ATTRIBUTES=['givenName', "homeDirectory", "uid"], - LDAP_AUTH_USER_FIELDS={ + LDAP_AUTH_ATTRIBUTES=['givenName', "homeDirectory", "uid"], + LDAP_AUTH_USER_FIELDS={ "username": "uid", "first_name": "givenName", "last_name": "homeDirectory", @@ -436,7 +435,6 @@ def testSyncUsersCreatesUsers(self): self.assertEqual(user.email, '') - @skipUnless(settings.LDAP_AUTH_TEST_USER_USERNAME, "No settings.LDAP_AUTH_TEST_USER_USERNAME supplied.") @skipUnless(settings.LDAP_AUTH_TEST_USER_PASSWORD, "No settings.LDAP_AUTH_TEST_USER_PASSWORD supplied.") @skipUnless(settings.LDAP_AUTH_USER_LOOKUP_FIELDS == ("username",), "Cannot test using custom lookup fields.")