Code

Opened 5 years ago

Closed 4 years ago

Last modified 4 years ago

#11526 closed (wontfix)

LDAP authentication backend

Reported by: psagers Owned by: dp_wiz
Component: contrib.auth Version: master
Severity: Keywords: ldap
Cc: silas@…, christoph.neuroth@…, listuser@…, ewoud+django@…, django@…, jeffschroeder@…, francois@…, django@…, cstejerean@…, hr.bjarni+django@…, danfairs Triage Stage: Design decision needed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

There are two other tickets on LDAP authentication. #2507 shows no signs of moving to completion. #7282 is a fairly general future design suggestion. I would like to see LDAP authentication in Django, so I'm submitting here a robust implementation with full documentation and unit tests.

Key features:

  • Authentication by either direct bind or search/bind.
  • Extensible group support with included implementations for groupOfUniqueNames (flat or nested) and posixGroup. Additional mechanisms can be supported by subclassing an abstract base class and implementing at least one API.
  • Optional propagation of LDAP attributes to user and profile fields. Boolean fields can be set according to group membership.
  • Optionally calculates group permissions based on LDAP group membership and a configurable mapping from LDAP groups to Django groups.

Logistics:

  • Implementation is located in django/contrib/auth/contrib/ldap/. Of necessity, it is split into two modules: one with the backend itself and the other with classes used for configuration and support. The second is safe to import into settings.py.
  • Documentation is primarily in docs/howto/auth-ldap.txt. Settings are added to docs/ref/settings.txt. Appropriate links added to other files.
  • Unit tests include a mock ldap test harness. The python-ldap module will not be imported during unit tests. Tests pass in Python 2.3, 2.4, 2.5, and 2.6 both in the context of a project and from tests/runtests.py.

Notes:

In addition to the unit tests, I have tested this against slapd on GNU/Linux; I haven't gotten access to other LDAP servers for testing yet. It would be good to do a pass against ActiveDirectory at least.

For grouping mechanisms, I've included support for posixGroup and groupOfUniqueNames. It's easy to add more, either before or after the initial commit.

The documentation lists versionadded as 1.1, which is obviously a fib. It should be updated to the current release version if and when it's checked in.

Attachments (2)

Authentication using LDAP.pdf (179.1 KB) - added by psagers 5 years ago.
auth_ldap.diff (104.6 KB) - added by psagers 4 years ago.
SVN revision 11638

Download all attachments as: .zip

Change History (32)

comment:1 Changed 5 years ago by cornbread

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Great contribution!

comment:2 Changed 5 years ago by psagers

I'd like to keep this ticket cleaner than #2507, so if anyone has patches for bug fixes or enhancements, please try sending them to me directly at psagersvUbwf9KKcH@…. I'll update the diff periodically while the ticket is open.

To read the documentation, apply the diff, rebuild your docs, and open howto/auth-ldap.html. If this is a problem, contact me and I can send a copy.

Thanks

comment:3 Changed 5 years ago by silas

  • Cc silas@… added

comment:4 Changed 5 years ago by anonymous

  • Cc christoph.neuroth@… added; silas@… removed

comment:5 Changed 5 years ago by anonymous

  • Cc silas@… added

comment:6 Changed 5 years ago by nix

  • Cc listuser@… added

comment:7 Changed 5 years ago by Alex

  • Triage Stage changed from Unreviewed to Design decision needed

comment:8 Changed 5 years ago by psagers

I've published this on PyPI as django-auth-ldap to make it easier to access and test.

comment:9 Changed 5 years ago by ekohl

I'm having trouble with the isinstance checks. If I put the group type directly in config.py, there's no problem, but if I put it in local_settings.py which is imported by settings.py, the check fails and the exception is raised. When I print the object, it's <django_auth_ldap.config.PosixGroupType object at 0x8be4d2c> and I can't figure out what I've done wrong.

I'm running Python 2.6.4rc1 and Django 1.1.1-1ubuntu1 on Ubuntu Karmic (9.10).

comment:10 follow-up: Changed 5 years ago by ekohl

According to http://www.canonical.org/~kragen/isinstance/ reload might cause this behaviour. It also makes a case against isinstance usage in python. In my downloaded copy I've removed the checks and that does solve the problem. The is_staff and is_superuser are properly mapped and groups are mirrored.

Thank you for the work you've put into it and I'm hoping this will find its way to django.

comment:11 in reply to: ↑ 10 Changed 5 years ago by psagers

Thanks for the report, ekohl. I haven't been able to reproduce it, but I'll take your word that it's possible to trigger. The only purpose of the isinstance checks was to catch the handful of users who will use an LDAPGroupType class instead of an instance. I wanted to provide a friendly configuration error in that case. That said, a known blocking bug is more compelling than a speculative configuration aid, so I took out the calls.

comment:12 Changed 5 years ago by ekohl

  • Cc ewoud+django@… added

Thanks, will try your update tomorrow at work.

Btw, we do use Fedora Directory Server in combination with posixAccount/posixGroup which behaves (in my experience) exactly the same as OpenLDAP from a query point of view.

comment:13 Changed 5 years ago by ekohl

I've tested and it seems to work fine. I'm going to try this on the production site which is currently forced to one shared account for a group of people.

One minor improvement would be to remove the included ._ files from the tarbal. These are Apple auto-created backup files and therefore redundant.

comment:14 Changed 5 years ago by ekohl

While testing on Centos 5.3 (which runs python 2.4 with python-ldap 2.2.0) I had problems with backend.py:304:

username = self.ldap.dn.escape_dn_chars(self._username)

It said
'module' object has no attribute 'dn'
Changing this into the following solved it.

from ldap import dn
username = dn.escape_dn_chars(self._username)

Note that I had no trouble with this on Ubuntu 9.10 (python 2.6 / python-ldap 2.3.8-1ubuntu1).

comment:15 Changed 5 years ago by psagers

I'll upload a diff with a fix for the ldap.dn import. It should be in config.py alongside the ldap.filter import. This update also includes fixes for handing Unicode usernames and attribute values. It seems python-ldap deals exclusively in utf8-encoded strings, so we need to encode and decode everything ourselves. I'll try to remember to build the distributions on a Linux machine to avoid the OS X metadata.

Thank

Changed 5 years ago by psagers

comment:16 Changed 4 years ago by wogan

  • Cc django@… added

comment:17 Changed 4 years ago by SEJeff

  • Cc jeffschroeder@… added

Has there been any traction on getting this in Django 1.x proper? I'm able to test on an Active Directory or openldap setup.

comment:18 Changed 4 years ago by rh0dium

  • Has patch unset
  • Patch needs improvement set

I have a couple of questions. Here is what I did.

  • Pulled the diff and applied the patch (patch -p0 auth_ldap.diff) to a stock 1.1 django release. It complained about the docs but then I figured out that if I was working off of the trunk it would be there..
  • Read the docs.
  • Now I put this in my global settings.py
AUTHENTICATION_BACKENDS = ( 
    'django.contrib.auth.contrib.ldap.backend.LDAPBackend', 
    'django.contrib.auth.backends.ModelBackend', 
) 
AUTH_LDAP_SERVER_URI = "ldap://discovery.smsc.com" 
AUTH_LDAP_USER_DN_TEMPLATE = "cn=%(user)s,o=SMSC,objectclass=person"

Fired up my test server (python2.6 manage.py shell --settings=settings_sklass_dev --pythonpath=/Users/sklass/perforcedev/tools/django/smscapps/)

Now when I login I get an exception:

   Error importing authentication backend django.contrib.auth.contrib.ldap.backend: "No module named contrib.ldap.backend"

It looks like the patch missed a file??

Changed 4 years ago by psagers

SVN revision 11638

comment:19 Changed 4 years ago by psagers

  • Has patch set
  • Patch needs improvement unset

The files are all there. If you look at the diff in Trac, you'll see them. As you say, it needs to be applied to the trunk (revision 11638 currently), not the django Python package. Feel free to email if you have trouble installing. Also note that the same code is currently available as a standalone package on pypi (django-auth-ldap), if you want an easier way to test it.

comment:20 Changed 4 years ago by dp_wiz

  • Owner changed from nobody to dp_wiz
  • Status changed from new to assigned

I configured django_auth_ldap for our ActiveDirectory server and it allows to log in using empty password.

comment:21 Changed 4 years ago by slurms

Fantastic :). The only annoying thing (imho) is often you have a set of users in an LDAP database somewhere that you have to keep in sync with the set of users in your application's users, so that issuing a User.objects.all() lets you traverse the full list of users for one reason or another. What I think would be awesome is with the multi-db stuff hitting trunk soon, you could have an LDAP db backend and use that to store your users and keep the rest of your data in another database... which would make the ordinary ModelBackend auth backend perfectly useful. Just a thought.

comment:22 Changed 4 years ago by frans

  • Cc francois@… added

comment:23 Changed 4 years ago by Ido <ido+django@…>

  • Cc django@… added

comment:24 Changed 4 years ago by offbytwo

  • Cc cstejerean@… added

comment:25 Changed 4 years ago by danfairs

  • Cc danfairs added

comment:26 Changed 4 years ago by hejsan

  • Cc hr.bjarni+django@… added

comment:27 follow-up: Changed 4 years ago by psagers

  • milestone set to 1.2

Moving to 1.2 so it will get closed.

comment:28 in reply to: ↑ 27 Changed 4 years ago by kmtracey

  • milestone 1.2 deleted

Replying to psagers:

Moving to 1.2 so it will get closed.

Please don't do that. 1.2 is now in bug fix only mode, trying to garner attention for features by putting them in the milestone just takes away devs time from the work of actually trying to fix bugs and get 1.2 out. Per http://code.djangoproject.com/wiki/Version1.2Features#Lowpriority this is a low-priority 1.2 feature that didn't make the feature deadline; at this point it will have to wait for the next release.

comment:29 Changed 4 years ago by psagers

  • Resolution set to wontfix
  • Status changed from assigned to closed

Sorry, by closed I meant wontfix. Closing wontfix myself on the advice of jkocherhans (http://groups.google.com/group/django-developers/browse_thread/thread/bb74fcc467917996).

This project has been on pypi for a while under the name django-auth-ldap (http://pypi.python.org/pypi/django-auth-ldap/). The source is hosted at http://bitbucket.org/psagers/django-auth-ldap/. Feedback and suggestions are welcome.

comment:30 Changed 4 years ago by ekohl

Thank you for your effort. I've been using it in production without issues for some time now.

Perhaps you could tag your releases in mercurial as well.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.