etianen / django-python3-ldap Goto Github PK
View Code? Open in Web Editor NEWDjango LDAP user authentication backend for Python 3.
License: BSD 3-Clause "New" or "Revised" License
Django LDAP user authentication backend for Python 3.
License: BSD 3-Clause "New" or "Revised" License
I have ben trying to get this working on a small site, with ad integration. I have come as far as i have it importing my users just fine, but i cant seem to be able to log into the site with them.
i have these settings in my settings.py and as i said i can sync the users just fine. But when i look at them in the django admin, it says their passwords are empty/not set?
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'django_python3_ldap.auth.LDAPBackend',
)`
`#LDAP START
# The URL of the LDAP server.
LDAP_AUTH_URL = "ldap://10.10.0.21:389"
# Initiate TLS on connection.
LDAP_AUTH_USE_TLS = False
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "OU=.site.local,DC=site,DC=local"
# The LDAP class that represents a user.
#LDAP_AUTH_OBJECT_CLASS = "inetOrgPerson"
LDAP_AUTH_OBJECT_CLASS = "organizationalPerson"
# User model fields mapped to the LDAP
# attributes that represent them.
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
# A tuple of django model fields used to uniquely identify a user.
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
# Path to a callable that takes a dict of {model_field_name: value},
# returning a dict of clean model data.
# Use this to customize how data loaded from LDAP is saved to the User model.
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
# Path to a callable that takes a user model and a dict of {ldap_field_name: [value]},
# and saves any additional user relationships based on the LDAP data.
# Use this to customize how data loaded from LDAP is saved to User model relations.
# For customizing non-related User model fields, use LDAP_AUTH_CLEAN_USER_DATA.
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
# Path to a callable that takes a dict of {ldap_field_name: value},
# returning a list of [ldap_search_filter]. The search filters will then be AND'd
# together when creating the final search filter.
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
# Path to a callable that takes a dict of {model_field_name: value}, and returns
# a string of the username to bind to the LDAP server.
# Use this to support different types of LDAP server.
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
# Sets the login domain for Active Directory users.
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = None
# The LDAP username and password of a user for authenticating the `ldap_sync_users`
# management command. Set to None if you allow anonymous queries.
LDAP_AUTH_CONNECTION_USERNAME = '[email protected]'
LDAP_AUTH_CONNECTION_PASSWORD = 'xxxxxx'
#LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "site.local"
#LDAP SLUT`
If i manualy define a password for the user in admin page, i can login just fine... so what am i missing?
i also tried to map the password field like this with no luck
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
"password": "userPassword",
}
Another isue i have, is the users is only being created when i use the sync command, not when they try to login? do i need to add anything to my views.py?
config snippet:
`LDAP_AUTH_SEARCH_BASE = "ou=Benutzer,dc=test,dc=domain,dc=de"
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "test.doamin.de"
LDAP_AUTH_CONNECTION_USERNAME = "username"
LDAP_AUTH_CONNECTION_PASSWORD = "password"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory_principal"
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "test.doamin.de"
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"loggers": {
"django_python3_ldap": {
"handlers": ["console"],
"level": "INFO",
},
},
}`
crashes with
`Traceback (most recent call last):
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\module_loading.py", line 23, in import_string
return getattr(module, class_name)
AttributeError: module 'django_python3_ldap.utils' has no attribute 'format_username_active_directory_principal'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "manage.py", line 22, in
execute_from_command_line(sys.argv)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\management_init_.py", line 363, in execute_from_command_line
utility.execute()
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\management_init_.py", line 355, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\management\base.py", line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\management\base.py", line 330, in execute
output = self.handle(*args, **options)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\contextlib.py", line 53, in inner
return func(*args, **kwds)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\management\commands\ldap_sync_users.py", line 17, in handle
password=settings.LDAP_AUTH_CONNECTION_PASSWORD,
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\contextlib.py", line 82, in enter
return next(self.gen)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\ldap.py", line 132, in connection
username = import_func(settings.LDAP_AUTH_FORMAT_USERNAME)(kwargs)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\utils.py", line 19, in import_func
return import_string(func)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\module_loading.py", line 27, in import_string
six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\six.py", line 685, in reraise
raise value.with_traceback(tb)
File "C:\Users\Tobias Liese\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\module_loading.py", line 23, in import_string
return getattr(module, class_name)
ImportError: Module "django_python3_ldap.utils" does not define a "format_username_active_directory_principal" attribute/class`
when running: python manage.py ldap_sync_users
Currently there is no setting for generic filtering of LDAP entries. It is only possible to filter for the object class, but needs would vary. For example, one may want to filter the search only to users that are members of a specific group.
I'm trying to authenticate against a legacy LDAPS server and get "dh key too small" errors when using TLS. The LDAP server supports stronger ciphers, but Python's SSL library appears to negotiate the weaker ones, which it then rejects.
$ nmap --script ssl-enum-ciphers ldap.example.com
| TLSv1.0:
| ciphers:
| TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA - E
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (dh 768) - E
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 768) - C
| TLS_DHE_RSA_WITH_DES_CBC_SHA (dh 768) - E
| TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA - F
| TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 - F
| TLS_DH_anon_WITH_3DES_EDE_CBC_SHA - F
| TLS_DH_anon_WITH_AES_128_CBC_SHA - F
| TLS_DH_anon_WITH_DES_CBC_SHA - F
| TLS_DH_anon_WITH_RC4_128_MD5 - F
| TLS_RSA_EXPORT_WITH_DES40_CBC_SHA - E
| TLS_RSA_EXPORT_WITH_RC4_40_MD5 - E
| TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_DES_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_NULL_MD5 (rsa 2048) - F
| TLS_RSA_WITH_NULL_SHA (rsa 2048) - F
| TLS_RSA_WITH_RC4_128_MD5 (rsa 2048) - C
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
I noticed ldap3
lets you set ciphers in the Tls
class constructor, and the problem goes away when I modify ldap3/core/tls.py
to hardcode a non-weak cipher (AES128-SHA
). It would be nice if there was a config option for this.
I tried adding django-python3-ldap to one of my Django projects. The project runs and displays the login page, but I get the following error when I try logging in with my credentials:
File "...\env\lib\site-packages\django_python3_ldap\ldap.py", line 162, in authenticate
return c.get_user(**kwargs)
File "...\AppData\Local\Continuum\Anaconda3\lib\contextlib.py", line 78, in exit
raise RuntimeError("generator didn't stop after throw()")
Running Django in debug mode, I can see the local variables within ldap.py's authenticate function at line 137 of ldap.py:
where the italics are replaced with my real Active Directory and user credentials. I can run the same ldap command without Django just from Python 3.5 and get an error "TrueFalse" is not defined from the bolded section above. If I change that to just "True", the connection is bind-able.
Is this a problem in django-python3-ldap or in the ldap library? I can't even see where the fast_decorder=TrueFalse comes from, but believe this is the reason for my LDAP authentication fails when trying to sign into my Django app.
I get this error when trying to login to /admin/
Traceback (most recent call last):
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/contrib/admin/sites.py", line 393, in login
return LoginView.as_view(**defaults)(request)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/contrib/auth/views.py", line 90, in dispatch
return super(LoginView, self).dispatch(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/views/generic/edit.py", line 182, in post
if form.is_valid():
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/forms/forms.py", line 183, in is_valid
return self.is_bound and not self.errors
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/forms/forms.py", line 175, in errors
self.full_clean()
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/forms/forms.py", line 385, in full_clean
self._clean_form()
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/forms/forms.py", line 412, in _clean_form
cleaned_data = self.clean()
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/contrib/auth/forms.py", line 187, in clean
self.user_cache = authenticate(self.request, username=username, password=password)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django/contrib/auth/__init__.py", line 100, in authenticate
user = backend.authenticate(*args, **credentials)
File "/Users/levy/Code/innri/venv/lib/python3.5/site-packages/django_python3_ldap/auth.py", line 23, in authenticate
return ldap.authenticate(*args, **kwargs)
TypeError: authenticate() takes 0 positional arguments but 1 was given
settings.py
:
...
AUTHENTICATION_BACKENDS = ("django_python3_ldap.auth.LDAPBackend",)
# The URL of the LDAP server.
LDAP_AUTH_URL = "ldap://myip:389"
# Initiate TLS on connection.
#LDAP_AUTH_USE_TLS = False
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "dc=ruv,dc=is"
# The LDAP class that represents a user.
#LDAP_AUTH_OBJECT_CLASS = "organizationalPerson"
# User model fields mapped to the LDAP
# attributes that represent them.
LDAP_AUTH_USER_FIELDS = {
'username': 'sAMAccountName',
'email': 'mail'
}
LDAP_AUTH_CONNECTION_USERNAME = '[email protected]'
LDAP_AUTH_CONNECTION_PASSWORD = 'mypass'
LDAP_AUTH_SEARCH_BASE = "dc=mydc1,dc=mydc2"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
This project depends on ldap3 (https://pypi.python.org/pypi/ldap3) - version 2 of which was released on 27th Oct 2016. This incorporates the following warning:
In version 2 of ldap3 some default values have been changed and the ldap3 namespace has been decluttered, removing redundant constants (look at the changelog for details). Also, the result code constants were moved to ldap3.core.results and the ldap3 custom exceptions were stored in ldap3.core.exceptions. If you experience errors in your existing code you should rearrange the import statements or explicitly set the defaults to their former values.
I believe that this means that django-python3-ldap won't work with ldap3>=2.0 - it would be great if it was updated to take advantage of the latest version!
edit: a solution to this is to just override LDAP_AUTH_FORMAT_USERNAME
but I wonder if a change to how we map these values would just be a more general and applicable solution?
Hey @etianen!
Thank you for all the work you have done on this library. I encountered an issue today when setting it up. The command ldap_sync_users
calls ldap.connection(username=...
making it so if you have a username
Django field that has a different label than username
it will produce an error in utils.py L#38
when it tries to pop username
from the LDAP_AUTH_USER_FIELDS
and it doesn't find the key username
.
In my instance my username
is defined as university_username
in my Django Model
I am trying to think of how this could be generalized, it would require that we either accept a command line argument that represents the username
field in our model with the default fallback being username
or we look into LDAP_AUTH_USER_FIELDS
and take they keyword from there. The problem is I am not sure how we can always know which field would link to username, because what if the user model was based on email
as the username or another field.
I guess the issue here is authenticating to ldap with a username and password correlates to the LDAP_AUTH_USER_FIELDS
and it probably shouldn't have that dependency.
I think maybe the best thing to do would be provide an additional setting that you can apply that maps the ldap username
to the model field
---
Clarification as I have learned more about django-python3-ldap
Suppose a Django custom user model:
class User(AbstractBaseUser, PermissionsMixin):
USERNAME_FIELD = 'email'
email = models.EmailField(unique=True)
university_username = models.CharField(
verbose_name="Username",
max_length=255,
unique=True,
)
Then the following mappings would apply:
LDAP_AUTH_USER_LOOKUP_FIELDS = ("email",)
LDAP_AUTH_USER_FIELDS = {
"email": "mail",
"university_username": "uid"
}
So pretty standard except instead of username
we have university_username
, here is where the problems start to occur.
# ldap_sync_users.py
class Command(BaseCommand):
help = "Creates local user models for all users found in the remote LDAP authentication server."
@transaction.atomic()
def handle(self, *args, **kwargs):
verbosity = int(kwargs.get("verbosity", 1))
# passing in kwarg dict with key username NOT university_username
with ldap.connection(username=settings.LDAP_AUTH_CONNECTION_USERNAME, password=settings.LDAP_AUTH_CONNECTION_PASSWORD) as connection:
for user in connection.iter_users():
if verbosity >= 1:
self.stdout.write("Synced {user}".format(
user = user,
))
This causes issues when we are trying to later pop and map the fields in utils.py
def convert_model_fields_to_ldap_fields(model_fields):
"""
Converts a set of model fields into a set of corresponding
LDAP fields.
"""
print "convert_model", model_fields
print settings.LDAP_AUTH_USER_FIELDS
return {
# field_name will get 'username' when the dict contains 'university_username', KeyError results
settings.LDAP_AUTH_USER_FIELDS[field_name]: field_value
for field_name, field_value
in model_fields.items()
}
Now assuming for sake of argument we modify the ldap_sync_users.py
with this hack so we get past this issue
@transaction.atomic()
def handle(self, *args, **kwargs):
verbosity = int(kwargs.get("verbosity", 1))
# we simply assign university_username to kwarg so that it matches appropriately and can move on
with ldap.connection(university_username=settings.LDAP_AUTH_CONNECTION_USERNAME, password=settings.LDAP_AUTH_CONNECTION_PASSWORD) as connection:
Everything should work fine now. The question is can we generalize so that if you change the username
field in your model it will pass in the appropriate kwarg
in the command?
I was running into authentication issues, so I ran the tests against my config and got the following result
Creating test database for alias 'default'...
..FF..F..F..F....E.F
======================================================================
ERROR: testRepeatedUserAuthenticationDoestRecreateUsers (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 128, in testRepeatedUserAuthenticationDoestRecreateUsers
self.assertEqual(user_1.pk, user_2.pk)
AttributeError: 'NoneType' object has no attribute 'pk'
======================================================================
FAIL: testAuthenticateUserSuccess (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 101, in testAuthenticateUserSuccess
self.assertIsInstance(user, User)
AssertionError: None is not an instance of <class 'django.contrib.auth.models.User'>
======================================================================
FAIL: testAuthenticateWithTLS (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 136, in testAuthenticateWithTLS
self.assertIsInstance(user, User)
AssertionError: None is not an instance of <class 'django.contrib.auth.models.User'>
======================================================================
FAIL: testGetUserArgsSuccess (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 40, in testGetUserArgsSuccess
self.assertIsInstance(user, User)
AssertionError: None is not an instance of <class 'django.contrib.auth.models.User'>
======================================================================
FAIL: testGetUserKwargsSuccess (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 63, in testGetUserKwargsSuccess
self.assertIsInstance(user, User)
AssertionError: None is not an instance of <class 'django.contrib.auth.models.User'>
======================================================================
FAIL: testLazySettingsClassLookup (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 31, in testLazySettingsClassLookup
self.assertEqual(settings.__class__.LDAP_AUTH_TEST_USER_USERNAME.default, "")
AssertionError: 'testuser' != ''
- testuser
+
======================================================================
FAIL: testSyncUsersCreatesUsers (django_python3_ldap.tests.TestLdap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Usher\SourceTree\webapps\webapps\django_python3_ldap\tests.py", line 143, in testSyncUsersCreatesUsers
self.assertGreater(User.objects.count(), 0)
AssertionError: 0 not greater than 0
----------------------------------------------------------------------
Ran 20 tests in 0.323s
FAILED (failures=6, errors=1)
Destroying test database for alias 'default'...
is there a way to create groups in the django model based on LDAP settings? Does anyone have any example or could point in right direction if its possible.
Hi
I have developed a Django app for the University of Cape Town and I am currently trying to hook it up with its Active Directory service.
I have tested python-ldap with Python 2.7.12 (default, Nov 19 2016, 06:48:10)
import ldap
conn = ldap.initialize('ldap://')
conn = ldap.initialize('ldap://137.158.157.86')
conn.protocol_version = 3
conn.set_option(ldap.OPT_REFERRALS, 0)
conn.simple_bind_s('[email protected]','password')
(97, [], 1, [])
which seems to work fine.
I have installed django-python3-ldap as outlined in the README and put the following lines in ./myapp/settings.py (Django 1.10)
AUTHENTICATION_BACKENDS = [
'django_python3_ldap.auth.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
]
LDAP_AUTH_URL = "ldap://msldap.uct.ac.za:636"
LDAP_AUTH_USE_TLS = False
LDAP_AUTH_SEARCH_BASE = "OU=UCT,DC=wf,DC=uct,DC=ac,DC=za"
LDAP_AUTH_OBJECT_CLASS = "inetOrgPerson"
LDAP_AUTH_USER_FIELDS = {
"username": "uid",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "wf.uct.ac.za"
LDAP_AUTH_CONNECTION_USERNAME = None
LDAP_AUTH_CONNECTION_PASSWORD = None
Executing the test command: python manage.py ldap_sync_users produces the following which seems like no users have been found:
LDAP connect failed: error receiving data: [Errno 104] Connection reset by peer
Traceback (most recent call last):
File "manage.py", line 22, in
execute_from_command_line(sys.argv)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/init.py", line 367, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python2.7/dist-packages/django/core/management/init.py", line 359, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 294, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 345, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py", line 185, in inner
return func(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django_python3_ldap/management/commands/ldap_sync_users.py", line 19, in handle
for user in connection.iter_users():
AttributeError: 'NoneType' object has no attribute 'iter_users'
I suspect it might be something quite trivial. Please advise.
Sebastian
Hi,
Certificate validation is rather simple :
import ssl # standard library
import certifi # new dependency
tls = ldap3.Tls(validate=ssl.CERT_REQUIRED, ca_certs_path=certifi.where()) # optionally: version=ssl.PROTOCOL_TLSv1_2
server = ldap3.Server(settings.LDAP_AUTH_URL, tls=tls)
ldap3.Connection(server, user=username, password=password, auto_bind=auto_bind) as c:
I guess it requires a few settings to be flexible.
There's something I don't understand: auto_bind
is forced to AUTO_BIND_NONE
in anonymous mode (eg when running ldap_sync_users), even with LDAP_AUTH_USE_TLS=True
Thanks !
I'm quite new to Django and also LDAP.
Is there a simple way to make any user that uses LDAP authentification a staff member?
Hi there! Thanks for developing this.
I'm able to run ldap_sync_users and get the accounts that I expect. After doing so, I manually set is_staff and is_superuser to True for my own account and tried to login to the admin interface. All I get is the standard "Please enter the correct username and password for a staff account. Note that both fields may be case-sensitive."
I'm at a loss for where to start debugging. Can you suggest something, even a function I can monkey-patch and debug?
Dear, etianen!
I found a strange issue.
In our project there is a two settings files:
In the top of prod_settings we have an import line
from .settings import *
This code is failed because of necessity to import functions from this application:
LDAP_AUTH_CLEAN_USER_DATA = django_python3_ldap.utils.clean_user_data
import django_python3_ldap.utils
Traceback at the end of this letter.
So, I want to ask you: can we get rid of those imports?
For example we can switch them to import-strings. Something like this:
# settings.py
LDAP_AUTH_CLEAN_USER_DATA = 'django_python3_ldap.utils.clean_user_data'
# ldap.py _get_or_create_user
from django.utils.module_loading import import_string
user_fields = import_string(settings.LDAP_AUTH_CLEAN_USER_DATA)
I will be glad to do that, if you ask me to.
Thanks!
Traceback example
Traceback (most recent call last):
File "/home/mnach/src/internal/manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
utility.execute()
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/management/__init__.py", line 190, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/management/__init__.py", line 40, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/usr/local/python/3.4.3/lib/python3.4/importlib/__init__.py", line 109, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
File "<frozen importlib._bootstrap>", line 1129, in _exec
File "<frozen importlib._bootstrap>", line 1471, in exec_module
File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/management/commands/runserver.py", line 14, in <module>
from django.db.migrations.executor import MigrationExecutor
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/migrations/executor.py", line 6, in <module>
from .loader import MigrationLoader
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/migrations/loader.py", line 10, in <module>
from django.db.migrations.recorder import MigrationRecorder
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/migrations/recorder.py", line 9, in <module>
class MigrationRecorder(object):
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/migrations/recorder.py", line 23, in MigrationRecorder
class Migration(models.Model):
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/migrations/recorder.py", line 24, in Migration
app = models.CharField(max_length=255)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/models/fields/__init__.py", line 1081, in __init__
super(CharField, self).__init__(*args, **kwargs)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/db/models/fields/__init__.py", line 161, in __init__
self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 48, in __getattr__
self._setup(name)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 44, in _setup
self._wrapped = Settings(settings_module)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 92, in __init__
mod = importlib.import_module(self.SETTINGS_MODULE)
File "/usr/local/python/3.4.3/lib/python3.4/importlib/__init__.py", line 109, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/home/mnach/src/internal/internal/prod_settings.py", line 4, in <module>
from .settings import *
File "/home/mnach/src/internal/internal/settings.py", line 221, in <module>
import django_python3_ldap.utils
File "/home/mnach/src/internal/django_python3_ldap/utils.py", line 12, in <module>
from django.contrib.auth.hashers import make_password
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/contrib/auth/__init__.py", line 7, in <module>
from django.middleware.csrf import rotate_token
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/middleware/csrf.py", line 14, in <module>
from django.utils.cache import patch_vary_headers
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/utils/cache.py", line 26, in <module>
from django.core.cache import caches
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/core/cache/__init__.py", line 34, in <module>
if DEFAULT_CACHE_ALIAS not in settings.CACHES:
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 48, in __getattr__
self._setup(name)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 44, in _setup
self._wrapped = Settings(settings_module)
File "/home/mnach/src/internal/env-3.4.3/lib/python3.4/site-packages/django/conf/__init__.py", line 113, in __init__
raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty.```
Hello, im new to this django and ldap stuff.. after trying hard and failed to install django-ldap for python 3 on my windows os, i found this i hope "savior".
Pretty easy it installed with no problems using pip install,
after it said done i included on installed apps.
My problem is i don't know how to configure the settings, since im not too familiar with them appart from the ldap server's url.
Im running django 1.7, any advice on settings?
Also i found some example of LDAP backend authentications using django-ldap, will they work with this module?
Thanks very much in advance and hope for your answer.
Hi,
I just configured my settings.py as following :
LDAP_AUTH_URL = 'ldap://ldap-icd.app.myLDAP.com:389'
LDAP_AUTH_SEARCH_BASE = "dc=Internal,dc=Users,dc=root"
LDAP_AUTH_OBJECT_CLASS = "*"
LDAP_AUTH_USER_FIELDS = {
"username": "uid",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = "username"
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_openldap"
LDAP_AUTH_CONNECTION_USERNAME = None
LDAP_AUTH_CONNECTION_PASSWORD = None
And when I call it from my command line : py manage.py ldap_sync_users
I got the following error :
C:\Python27\lib\site-packages\django\core\management\base.py:577: RemovedInDjang o110Warning: NoArgsCommand class is deprecated and will be removed in Django 1.1 0. Use BaseCommand instead, which takes no arguments by default. RemovedInDjango110Warning
Do you have any idea why I'm facing this issue?
Thanks
Maybe you add this code.
In my case, he helped.
def format_username_active_directory_dog(model_fields):
username = model_fields["username"]
if settings.LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN:
username = "{username}@{domain}".format(
domain=settings.LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN,
username=username,
)
return username
Request Method: POST
Request URL: http://laisydev.slnet.bov.lc:8483/parcelmanager/authenticatedLogin/
Django Version: 1.8
Exception Type: ImportError
Exception Value:
No module named 'django_python3_ldap.auth'
Exception Location: /usr/lib/python3.5/importlib/init.py in import_module, line 126
Python Executable: /home/goslnet.gov.lc/yfevrier/git/laisy/venv/laisyvenv/bin/python3.5
Python Version: 3.5.2
Python Path:
['/home/goslnet.gov.lc/yfevrier/git/laisy',
'/usr/lib/python3.5',
'/usr/lib/python3.5/plat-x86_64-linux-gnu',
'/usr/lib/python3.5/lib-dynload',
'/home/goslnet.gov.lc/yfevrier/git/laisy/venv/laisyvenv/lib/python3.5/site-packages',
'/usr/lib/python35.zip']
Server time: Fri, 13 Jan 2017 09:01:04 -0600
Request Method: POST
Request URL: http://laisydev.slnet.bov.lc:8483/parcelmanager/authenticatedLogin/
Django Version: 1.8
Python Version: 3.5.2
Installed Applications:
('django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_python3_ldap',
'crispy_forms',
'django_tables2',
'captcha',
'nocaptcha_recaptcha',
'parcelmanager',
'surveyplanmanager')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/goslnet.gov.lc/yfevrier/git/laisy/parcelmanager/views.py" in authenticatedLogin
131. user = authenticate(username=username, password=password)
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in authenticate
66. for backend, backend_path in _get_backends(return_tuples=True):
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in _get_backends
27. backend = load_backend(backend_path)
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in load_backend
21. return import_string(path)()
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/utils/module_loading.py" in import_string
26. module = import_module(module_path)
File "/usr/lib/python3.5/importlib/init.py" in import_module
126. return _bootstrap._gcd_import(name[level:], package, level)
Exception Type: ImportError at /parcelmanager/authenticatedLogin/
Exception Value: No module named 'django_python3_ldap.auth'
Hi !
Just tested your module with python 2.7.
Apart from classes that have to inherit from object
, there does not appear to have too many things to fix to support python2.7.
Is that feasable ?
That would allow for a seamless upgrade from py2 to py3 for your users, and avoiding yet another dependency (django-auth-ldap
, with its dependency on python-ldap
) ..
Thanks !
It took one hour to figure this out, so I will leave it here for a documentation enhancement proposal.
Maybe other people will have to this
LDAP_AUTH_CONNECTION_USERNAME = 'cn=admin,dc=example,dc=com'
LDAP_AUTH_FORMAT_USERNAME = lambda x: x['username']
LDAP_AUTH_SEARCH_BASE = 'ou=users,dc=example,dc=com'
I don't know enough about LDAP and related protocols, but in my case I had to provide a quick fix to the login credentials in the Django settings file.
If I didn't done the right adjustment, please someone tell me.
Is there any way to use local Django username/password details if the LDAP login fails?
I'd like to have some standard admin and management logins regardless of the LDAP server being used.
I have usernames as "Pupkin.V.I", and search query failed, becouse (line 98 ldap.py):
field_value = clean_ldap_name(field_value),
When i changed to
field_value = field_value, # clean_ldap_name(field_value),
query is success
is it on the roadmap?
When installing using pip, the install fails with error:
(harava_env)securejava@debian:~/projects/harava$ pip install django-python3-ldap
Collecting django-python3-ldap
Downloading django-python3-ldap-0.9.3.tar.gz
Requirement already satisfied (use --upgrade to upgrade): django>=1.7 in /home/securejava/projects/harava_env/lib/python3.4/site-packages (from django-python3-ldap)
Collecting ldap3==0.9.8.2 (from django-python3-ldap)
Could not find a version that satisfies the requirement ldap3==0.9.8.2 (from django-python3-ldap) (from versions: 0.9.8.2.post1, 0.9.8.3, 0.9.8.3.post1, 0.9.8.4)
No matching distribution found for ldap3==0.9.8.2 (from django-python3-ldap)
(harava_env)securejava@debian:~/projects/harava$ pip install ldap3pip --version
Collecting ldap3pip
Could not find a version that satisfies the requirement ldap3pip (from versions: )
No matching distribution found for ldap3pip
(harava_env)securejava@debian:~/projects/harava$ pip --version
pip 7.0.3 from /home/securejava/projects/harava_env/lib/python3.4/site-packages (python 3.4)
The pip version I'm using is 7.0.3.
Hello @etianen,
I have a custom User model like this:
class User(AbstractBaseUser, PermissionsMixin):
identifier = models.UUIDField(default=uuid.uuid4, unique=True)
email = models.EmailField(max_length=255, unique=True, null=True)
USERNAME_FIELD = 'identifier'
I want to authenticate to ldap using the 'email' field instead of the 'identifier' field which is set in USERNAME_FIELD. I do not want to change USERNAME_FIELD because other authentication backends will use the 'identifier' field for authentication. By looking in the code, it seems that I have to manipulate the resolve_user_identifier function to do this. So, do you think that an LDAP_AUTH_RESOLVE_USER_IDENTIFIER setting for passing a custom callable similar to LDAP_AUTH_CLEAN_USER_DATA is a good idea? Or is there another way?
I am getting a KeyError in ldap.py on a successful login in the dictionary user_data
:
INFO:ldap3:ldap3 library initialized - logging emitted with loglevel set to DEBUG - available detail levels are: OFF, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED - sensitive data will be hidden
ERROR:django.request:Internal Server Error: /autnenticate/
Traceback (most recent call last):
File "/websites/rnt/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/websites/rnt/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/websites/rnt/home/views.py", line 43, in autnenticate
userauth = authenticate(username=_ad_user, password=_ad_pass)
File "/websites/rnt/venv/lib/python3.5/site-packages/django/contrib/auth/__init__.py", line 74, in authenticate
user = backend.authenticate(**credentials)
File "/websites/rnt/venv/lib/python3.5/site-packages/django_python3_ldap/auth.py", line 23, in authenticate
return ldap.authenticate(*args, **kwargs)
File "/websites/rnt/venv/lib/python3.5/site-packages/django_python3_ldap/ldap.py", line 157, in authenticate
return c.get_user(**kwargs)
File "/websites/rnt/venv/lib/python3.5/site-packages/django_python3_ldap/ldap.py", line 101, in get_user
return self._get_or_create_user(self._connection.response[0])
File "/websites/rnt/venv/lib/python3.5/site-packages/django_python3_ldap/ldap.py", line 41, in _get_or_create_user
attributes = user_data["attributes"]
KeyError: 'attributes'
user_data
:
INFO:ldap3:ldap3 library initialized - logging emitted with loglevel set to DEBUG - available detail levels are: OFF, ERROR, BASIC, PROTOCOL, NETWORK, EXTENDED - sensitive data will be hidden
INFO:root:<class 'dict'>
INFO:root:['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
INFO:root:{'uri': ["b'ldaps://ForestDnsZones.domain.com/DC=ForestDnsZones,DC=domain,DC=com'"], 'type': 'searchResRef'}
The full configuration:
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend',
)
LDAP_AUTH_URL = "ldaps://domain.com"
LDAP_AUTH_USE_TLS = True
LDAP_AUTH_SEARCH_BASE = "dc=domain,dc=com"
LDAP_AUTH_OBJECT_CLASS = "userClass"
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "DOMAIN"
LDAP_AUTH_CONNECTION_USERNAME = 'user'
LDAP_AUTH_CONNECTION_PASSWORD = 'pass'
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"loggers": {
"django_python3_ldap": {
"level": "DEBUG",
},
},
}
The login fails silently when the bound user doesn't have permission to read the attributes of the bound user object during login.
The OpenLDAP logs show a 32 error code when this occurs. I haven't checked whether the response from the OpenLDAP sever includes the error code, though:
SEARCH RESULT tag=101 err=32 nentries=0 text=
It would be useful for django-python3-ldap to log a warning when this occurs (e.g., similar to the one for invalid credentials), as the error likely signals a configuration problem.
if (
(settings.LDAP_AUTH_CONNECTION_USERNAME or settings.LDAP_AUTH_CONNECTION_PASSWORD) and
(
settings.LDAP_AUTH_CONNECTION_USERNAME != username or
settings.LDAP_AUTH_CONNECTION_PASSWORD != password
)
):
try:
c.rebind(
user=format_username({"username": settings.LDAP_AUTH_CONNECTION_USERNAME}),
password=settings.LDAP_AUTH_CONNECTION_PASSWORD,
)
except LDAPException as ex:
logger.info("LDAP rebind failed: {ex}".format(ex=ex))
yield None
return
Hi,
I'm having an issue installing through pip. It's looking for python3-ldap version 0.9.5.3, which doesn't seem to exist (pip install python3-ldap==0.9.5.3
fails for me as well). Is there any way to use a different python3-ldap version?
Downloading/unpacking python3-ldap==0.9.5.3 (from django-python3-ldap)
Could not find a version that satisfies the requirement python3-ldap==0.9.5.3 (from django-python3-ldap) (from versions: 0.9.3.5, 0.9.4.2, 0.9.5.4, 0.9.6.2, 0.9.7, 0.8.3, 0.9.1, 0.9.2.2, 0.9.3.5, 0.
9.4.2, 0.9.5.4, 0.9.6.2, 0.9.7)
Cleaning up...
Thanks!
firas@MAChINE1-2-1:~/platform$ ./manage.py ldap_sync_users
LDAP connect failed: invalid server address
INFO:django_python3_ldap.ldap:LDAP connect failed: invalid server address
Traceback (most recent call last):
File "./manage.py", line 116, in <module>
execute_from_command_line([sys.argv[0]] + django_args)
File "/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 354, in execute_from_command_line
utility.execute()
File "/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 346, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/local/lib/python2.7/site-packages/django/core/management/base.py", line 394, in run_from_argv
self.execute(*args, **cmd_options)
File "/local/lib/python2.7/site-packages/django/core/management/base.py", line 445, in execute
output = self.handle(*args, **options)
File "/local/lib/python2.7/site-packages/django/utils/decorators.py", line 145, in inner
return func(*args, **kwargs)
File "/local/lib/python2.7/site-packages/django_python3_ldap/management/commands/ldap_sync_users.py", line 22, in handle
user=user,
File "/usr/lib/python2.7/contextlib.py", line 36, in __exit__
raise RuntimeError("generator didn't stop after throw()")
RuntimeError: generator didn't stop after throw()
Thanks for your all work!
When I use django_auth-ldap, I set the password by AUTH_LDAP_BIND_PASSWORD='123456'
, but I don't find how to do it in your work.
I've tried following the readme here, but can't seem to find the correct settings to make a connection to a test server that has anon access.
Is this supported? If so, what settings would be required?
Environment:
Request Method: POST
Request URL: http://laisydev.slnet.bov.lc:8483/parcelmanager/authenticatedLogin/
Django Version: 1.8
Python Version: 3.5.2
Installed Applications:
('django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_python3_ldap',
'crispy_forms',
'django_tables2',
'captcha',
'nocaptcha_recaptcha',
'parcelmanager',
'surveyplanmanager')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/goslnet.gov.lc/yfevrier/git/laisy/parcelmanager/views.py" in authenticatedLogin
131. user = authenticate(username=username, password=password)
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in authenticate
66. for backend, backend_path in _get_backends(return_tuples=True):
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in _get_backends
27. backend = load_backend(backend_path)
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/contrib/auth/init.py" in load_backend
21. return import_string(path)()
File "/srv/laisy/venv/laisyvenv/lib/python3.5/site-packages/django/utils/module_loading.py" in import_string
26. module = import_module(module_path)
File "/usr/lib/python3.5/importlib/init.py" in import_module
126. return _bootstrap._gcd_import(name[level:], package, level)
Exception Type: ImportError at /parcelmanager/authenticatedLogin/
Exception Value: No module named 'django_python3_ldap.auth'
Using version 0.9.12 and ldap3 version 2.2.0, authentication works, however, when the user is created in djangodb auth_user table the fields only contain the first letter of each attribute.
hey,
the changes introduced by this ticket raises some errors with the auth.py/ldap.py. The request parameter is now populated down to the authenticate() function => *args now contains now the request:
https://code.djangoproject.com/ticket/25187
I think the function signature mismatch between the ldap.py authenticate() and auth.py authenticate() causes the problems.
Hello!
I'm a Django newbie, but managed to install and use this LDAP Auth, synced all the users in a breeze! Thanks! :) I was wondering if it is possible to sync the LDAP groups also. We are running an old version of OpenLDAP where the membership is in the group attributes and NOT on the user's.
Thanks!
Is there a way to use a iterable of servers and make multiple attempts to connect to each? For example:
LDAP_AUTH_URL = ("ldap://0.0.0.1:389", "ldap://0.0.0.2:389", ldap://0.0.0.3:398",)
I'm getting the error below. This is a new setup. I'm very new to Django so it's very possible I'm doing something wrong. The error occurs during login. I did a packet capture and I can see that the auth is binding properly, so I would say this error happens after ldap binding.
Using:
Python 3.5.3
Django 1.10.6
ldap3 2.2.1
django-python3-ldap 0.9.11
Traceback (most recent call last):
File "C:\Users\jeff\django-ccp\lib\site-packages\django_python3_ldap\ldap.py", line 137, in connection
yield Connection(c)
File "C:\Users\jeff\django-ccp\lib\site-packages\django_python3_ldap\ldap.py", line 162, in authenticate
return c.get_user(**kwargs)
File "C:\Users\jeff\django-ccp\lib\site-packages\django_python3_ldap\ldap.py", line 101, in get_user
search_scope = ldap3.SEARCH_SCOPE_WHOLE_SUBTREE,
AttributeError: module 'ldap3' has no attribute 'SEARCH_SCOPE_WHOLE_SUBTREE'
Here are my settings:
LDAP_AUTH_URL = "ldap://ip.addr:389"
LDAP_AUTH_USE_TLS = False
LDAP_AUTH_SEARCH_BASE = "MY-OU-PATH"
LDAP_AUTH_OBJECT_CLASS = "person"
LDAP_AUTH_USER_FIELDS = {
'username': 'sAMAccountName',
'first_name': 'givenName',
'last_name': 'sn',
'email': 'mail',
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_ACTIVE_DIRECTORY_DOMAIN = "corp"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
In my place sometimes adding (objectCategory=person) to the search filter helps limiting the scope to 'person' objects only and hence improving the performance of the search query. I just wonder if we could have an optional SETTING entry for it, similar to ObjectClass.
Thanks.
Hi, first, I would like to thank you for your nice work. It's a pain in the .. to work with LDAP.
I just follow your instructions and your configuration like this :
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend'
)
LDAP_AUTH_URL = 'ldap://ldap-icd.app.my-ldap.com:389'
LDAP_AUTH_USE_TLS = False
LDAP_AUTH_SEARCH_BASE = "dc=Internal,dc=Users,dc=root"
LDAP_AUTH_OBJECT_CLASS = "person"
LDAP_AUTH_USER_FIELDS = {
"username": "uid",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_openldap"
LDAP_AUTH_CONNECTION_USERNAME = "uid=reader.myAPP,dc=Services,dc=Users,dc=root"
LDAP_AUTH_CONNECTION_PASSWORD = "myPass"
When I execute python manage.py ldap_sync_users
it's okay, all the username appears on the screen but when I try to logging through the administration interface, it's not working. My error message :
Please enter a correct username and password. Note that both fields are case-sensitive
I'm sure that both username and password are correct.
Any idea? Thanks
Hello,
Thanks for your clear documentation :)
I try to make working your module for the first time but I have some issue.
My LDAP server is particular and need the last stable version (2.2.3) of ldap3 python module.
You can see the issue corrected 2 days ago here : cannatag/ldap3#334
When I try to import ldap users in django with your module, it's not working.
You can see my try here :
gduale:~:$ . ./py361-test/bin/activate
(py361-test) gduale:~:$ pip install django-python3-ldap
Collecting django-python3-ldap
Using cached django-python3-ldap-0.9.14.tar.gz
Collecting django>=1.8 (from django-python3-ldap)
Using cached Django-1.11-py2.py3-none-any.whl
Collecting ldap3==2.2.0 (from django-python3-ldap)
Using cached ldap3-2.2.0-py2.py3-none-any.whl
Collecting pytz (from django>=1.8->django-python3-ldap)
Using cached pytz-2017.2-py2.py3-none-any.whl
Collecting pyasn1>=0.1.8 (from ldap3==2.2.0->django-python3-ldap)
Using cached pyasn1-0.2.3-py2.py3-none-any.whl
Installing collected packages: pytz, django, pyasn1, ldap3, django-python3-ldap
Running setup.py install for django-python3-ldap ... done
Successfully installed django-1.11 django-python3-ldap-0.9.14 ldap3-2.2.0 pyasn1-0.2.3 pytz-2017.2
Then I know that with ldap3 version 2.2.0 it will not work (I already try), so I install the latest version :
(py361-test) gduale:~:$ pip install --upgrade ldap3
Collecting ldap3
Using cached ldap3-2.2.3-py2.py3-none-any.whl
Requirement already up-to-date: pyasn1>=0.1.8 in ./py361-test/lib/python3.6/site-packages (from ldap3)
Installing collected packages: ldap3
Found existing installation: ldap3 2.2.0
Uninstalling ldap3-2.2.0:
Successfully uninstalled ldap3-2.2.0
Successfully installed ldap3-2.2.3
Then I try to sync the users, but it fails :
(py361-test) gduale:~/django-projects/test-c2i/c2i:$ python manage.py ldap_sync_users
Traceback (most recent call last):
File "manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/core/management/__init__.py", line 363, in execute_from_command_line
utility.execute()
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/core/management/__init__.py", line 355, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/core/management/base.py", line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/core/management/base.py", line 330, in execute
output = self.handle(*args, **options)
File "/home/gduale/make_python/python-3.6.1/lib/python3.6/contextlib.py", line 53, in inner
return func(*args, **kwds)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django_python3_ldap/management/commands/ldap_sync_users.py", line 21, in handle
for user in connection.iter_users():
File "/home/gduale/py361-test/lib/python3.6/site-packages/django_python3_ldap/ldap.py", line 90, in <genexpr>
if entry["type"] == "searchResEntry"
File "/home/gduale/py361-test/lib/python3.6/site-packages/django_python3_ldap/ldap.py", line 66, in _get_or_create_user
**user_lookup
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/db/models/query.py", line 475, in update_or_create
lookup, params = self._extract_model_params(defaults, **kwargs)
File "/home/gduale/py361-test/lib/python3.6/site-packages/django/db/models/query.py", line 531, in _extract_model_params
"', '".join(sorted(invalid_params)),
django.core.exceptions.FieldError: Invalid field name(s) for model User: 'd', 'i', 'u'.
Do you know if is it possible to make working your module with the latest stable version of ldap3 please ?
Thank you,
Best regards.
After provisioning a new VM with my app and dumping the DB from my dev db to it, I tried to login with the superuser account 'admin' that would have been setup by the provisioning script. It failed with this error
'module' object has no attribute 'LDAPBindError'
I realized my mistake was that the superuser would have the password from the dev db since I overwrote the new db completely. Logging in with that password works.
My test VM CANNOT connect to the LDAP server right now, so I fully expected an error about failure to connect. But from the looks of this, the exception handling isn't getting that far.
The issue is that the error isn't saying the password was wrong, or that the connection can't be made. It's saying the module doesn't have an attribute that the connection object should have to even tell which exception to throw. And I don't know why.
My setup has the builtin ModelBackend being checked before LDAP. So the correct superuser pw will avoid ever hitting the LDAP check, and therefore the error.
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'django_python3_ldap.auth.LDAPBackend',
]
But with the wrong password, the check is passed on to the next backend, django_python3_ldap, which fails to throw the correct exception.
Django Version: 1.10.4
Exception Type: AttributeError
Exception Value:
'module' object has no attribute 'LDAPBindError'
Exception Location: /srv/api/lib/python3.4/site-packages/django_python3_ldap/ldap.py in connection, line 138
Python Executable: /srv/api/bin/uwsgi
Python Version: 3.4.2
Python Path:
['/srv/api/',
'.',
'',
'/srv/api/lib/python3.4',
'/srv/api/lib/python3.4/plat-x86_64-linux-gnu',
'/srv/api/lib/python3.4/lib-dynload',
'/usr/lib/python3.4',
'/usr/lib/python3.4/plat-x86_64-linux-gnu',
'/srv/api/lib/python3.4/site-packages']
In ldap.py, There's a bit of code that forces the use of auto_bind if user_identifier is present. (Which it will be given it's called in the authenticate method, where the username/email/id must always be given, and this is passed through to connection). I'm encountering a server that will not let me connect unless auto_bind=AUTO_BIND_NONE
. Is there a workaround for this issue?
if user_identifier:
if settings.LDAP_AUTH_USE_TLS:
auto_bind = ldap3.AUTO_BIND_TLS_BEFORE_BIND
else:
auto_bind = ldap3.AUTO_BIND_NO_TLS
else:
auto_bind = ldap3.AUTO_BIND_NONE
hi
1st things 1st: thank you for sharing this codebase with the community!
I can't seem to find the right configuration for the django plugin though, I have a working ldap3 example and trying to match it in my settings but always getting
LDAP connect failed: LDAPInvalidCredentialsResult - 49 - invalidCredentials - None - 80090308: LdapErr: DSID-0C0903A8, comment: AcceptSecurityContext error, data 52e, v1db1 - bindResponse - None
the following is a working example of search I'm doing with ldap3
from ldap3 import Server, Connection, ALL
print('### SERVER')
server = Server('host.group.domain.com', get_info=ALL)
print('### CONNECT')
conn = Connection(
server,
'CN=ldap_user,OU=SERVICE,OU=100 Common Services Objects,OU=Common Services and Applications,DC=group,DC=domain,DC=com',
'ldap_password',
auto_bind=True)
print('### SEARCH')
conn.search('DC=group,DC=domain,DC=com', '(&(sAMAccountName=username_search)(objectclass=user))')
print('### ENTRIES')
print([e for e in conn.entries])
I tried, among other combinations, the following
LDAP_AUTH_URL = 'ldap://host.group.domain.com:389'
LDAP_AUTH_SEARCH_BASE = 'DC=group,DC=domain,DC=com'
LDAP_AUTH_USER_FIELDS = {
'username': 'sAMAccountName',
'first_name': 'givenName',
'last_name': 'sn',
'email': 'mail'
}
LDAP_AUTH_CONNECTION_USERNAME = 'CN=ldap_user,OU=SERVICE,OU=100 Common Services Objects,OU=Common Services and Applications,DC=group,DC=domain,DC=com'
LDAP_AUTH_CONNECTION_PASSWORD = 'ldap_password'
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
but always getting the invalid credential error. could you give a quick hint please? I'd be happy to help writing a bit of documentation if this use case is relevant for you
many thanks!
I'm trying to sync all my active directory users to my local django users, all attempts has been unsuccessful, the command gives me a KeyError: 'attributes' on attributes = user_data["attributes"]. Also the login doesn't work.
I'm using Django 1.8.6 with Python 3.4, here's my ldap settings:
LDAP_AUTH_URL = "ldaps://10.0.0.1:636"
LDAP_AUTH_USE_TLS = None
LDAP_AUTH_SEARCH_BASE = "DC=mydomain,DC=example,DC=com"
LDAP_AUTH_OBJECT_CLASS = "organizationalPerson"
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
LDAP_AUTH_CONNECTION_USERNAME = 'CN=admin,OU=Users,OU=MyGroup,OU=MyDomain,DC=mydomain,DC=example,DC=com'
LDAP_AUTH_CONNECTION_PASSWORD = 'f4ncyp4ssw0rd'
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
I modified the ldap.py module printint the value of user_data and gives me this output:
{'uri': ["b'ldaps://mydomain.example.com/CN=Configuration,DC=mydomain,DC=example,DC=com'"], 'type': 'searchResRef'}
So, the dictionary cames without the attributes value, what I am missing? Configuration error?
PS: Sorry by me terrible english ;)
I have a silly question. I seem to keep getting LDAP login failed: automatic bind not successful - invalidCredentials
error and I believe it has to do with wrong settings. I am having trouble mapping the settings. I used another app that used LDAP to authenticate (these settings worked). How would I translate the settings below, to use django-python3-ldap?
uri=ldap://chop.edu:3268
user_filter=objectClass=*
user_name_attr=sAMAccountName
bind_user=cn=Version Control,ou=AdminUsers,ou=Res,dc=research,dc=chop,dc=edu
bind_password=removed-password
basedn=dc=chop,dc=edu
search_scope=SUBTREE
I am currently using:
LDAP_AUTH_URL='ldap://chop.edu:3268'
LDAP_AUTH_CONNECTION_USERNAME='cn=Version Control,ou=AdminUsers,ou=Res,dc=research,dc=chop,dc=edu'
LDAP_AUTH_CONNECTION_PASSWORD='removed-password'
LDAP_AUTH_USE_TLS=False
LDAP_AUTH_SEARCH_BASE='dc=chop,dc=edu'
LDAP_AUTH_OBJECT_CLASS='objectClass=*'
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_SYNC_USER_RELATIONS = 'django_python3_ldap.utils.sync_user_relations'
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
Hi,
I've just created a fresh Django project with all default settings (default user model etc.) to test this.
I've installed django-python3-ldap as described, added 'django_python3_ldap' my INSTALLED_APPS and set AUTHENTICATION_BACKENDS = ['django_python3_ldap.auth.LDAPBackend'].
The rest of the settings I've overridden are:
LDAP_AUTH_URL = "[my server URL]"
LDAP_AUTH_SEARCH_BASE = "dc=[1st DC],dc=[2nd DC],dc=[3rd DC],dc=[4th DC]"
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"loggers": {
"django_python3_ldap": {
"handlers": ["console"],
"level": "DEBUG",
},
},
}
I used a python shell to test binding as follows:
from ldap3 import Server, Connection
s = Server('[Server URL]', get_info=ALL)
c = Connection(s, user='[username]', password='[right password]')
if not c.bind():
print("Error!", c.result)
else:
print("It Worked!", c.result)
It Worked! {'description': 'success', 'referrals': None, etc....}
c = Connection(s, user='[username]', password='[wrong password]')
if not c.bind():
print("Error!", c.result)
else:
print("It Worked!", c.result)
Error! {'description': 'invalidCredentials', 'referrals': None, etc....}
So now I'm trying to use this to log in to my django site. At first I was just trying to log into the admin site using this, but now I've built a proper login form in the front-end site just to check that this all works and it wasn't the admin login page doing something funky.
When I attempt to log in using an incorrect User & Pass, in my console I see:
[17/Mar/2017 12:56:27] "POST /accounts/login/ HTTP/1.1" 200 1135
LDAP login failed: automatic bind not successful - invalidCredentials
but when I use the correct User & Pass I only get:
[17/Mar/2017 12:56:27] "POST /accounts/login/ HTTP/1.1" 200 1135
The login page still says "Incorrect username and / or password! Please double-check and try again." and doesn't forward me on to the correct place, and there are no new user entries in my auth_user table in my DB.
Any idea's where I'm going wrong? It appears I have enough settings right for it to recognise bad credentials, but not enough right for it to do anything useful on a successful bind!
Any help appreciated!
Many thanks,
-Tim
First thanks for making this app available, its great. I've been looking at the code to see how I would implement multiple search bases? Would I have to create a custom backend subclassing a lot of the django_python3_ldap code for each search base like this in my settings.py
:
AUTHENTICATION_BACKENDS = ('my_custom_auth.auth.UKLDAPBackend', 'my_custom_auth.auth.DELDAPBackend', 'django.contrib.auth.backends.ModelBackend')
Referencing different search bases e.g.
LDAP_AUTH_UK_SEARCH_BASE = 'ou=engineering,ou=united kingdom,ou=my company users,dc=my company,dc=global,dc=corp' LDAP_AUTH_DE_SEARCH_BASE = 'ou=engineering,ou=germany,ou=my company users,dc=my company,dc=global,dc=corp'
Or is there something already built in that would support multiple search bases? I can't see something immediately obvious but I may well have missed it.
ldap search can return different kinds of responses:
searchResEntry
- it contains the attributes
key, all goodsearchResRef
- attributes
key is not present, and then _get_or_create_user
will raise an error.It's especially valid for iter_users
when both types can be present.
Quick workaround for iter_users
:
return (
self._get_or_create_user(entry)
for entry
in paged_entries if entry["type"] == "searchResEntry"
)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.