Django

Code

Ticket #2507 (new)

Opened 2 years ago

Last modified 4 weeks ago

[patch] LDAPBackend in django/contrib/auth/backends.py

Reported by: spr@mahonri5.net Assigned to: nobody
Component: Contrib apps Version: SVN
Keywords: Cc: wangchun@exoweb.net, hv@tbz-pariv.de, cmawebsite@gmail.com
Triage Stage: Accepted Has patch: 1
Needs documentation: 0 Needs tests: 0
Patch needs improvement: 1

Description

This patch provides a backend that will allow users to authenticate against LDAP. It creates User objects based on fields in LDAP.

Various LDAP settings are provided by the projects settings.py file.

Attachments

backends.py.diff (5.8 kB) - added by spr@mahonri5.net on 08/09/06 15:53:11.
patch
backends.py.2.diff (6.3 kB) - added by spr@mahonri5.net on 08/10/06 13:32:49.
update to allow ldap options, and custom bind string functions. Also fixes a typo.
backends.py.3.diff (7.7 kB) - added by spr@mahonri5.net on 08/10/06 14:37:54.
Fixing bug with BIND_STRING_FUNC and adding a method to be used with BIND_STRING_FUNC.
backends.py.4.diff (7.8 kB) - added by spr@mahonri5.net on 08/11/06 11:48:56.
Slight update to make the users experience better with the pre_auth_bind.
backends.py.5.diff (8.3 kB) - added by spr@mahonri5.net on 08/11/06 14:45:07.
Better handling of searches, and setting good defaults to mk_pre_auth_bind()
backends.py.6.diff (8.3 kB) - added by spr@mahonri5.net on 08/12/06 15:13:46.
fixing use of LDAP_OPTIONS hash so code follows documentation
ldap.diff (9.2 kB) - added by spr@mahonri5.net on 08/12/06 17:30:10.
Random password, and adding defaults to global_settings.py to avoid loading errors.
ldap.2.diff (10.0 kB) - added by spr@mahonri5.net on 08/14/06 23:21:48.
Update to allow inheritance of LDAPBackend, so you can override the User object and generation of the bind string. This is the full patch for LDAP auth support.
ldap.3.diff (10.2 kB) - added by spr@mahonri5.net on 08/15/06 17:10:34.
Fixes a few run-time bugs, adding some more error checking.
ldap.4.diff (10.2 kB) - added by spr@mahonri5.net on 08/16/06 22:19:17.
Shouldn't have removed staticmethod from mk_pre_auth_bind.
ldapauth.diff (10.4 kB) - added by Marc Fargas <telenieko@telenieko.com> on 02/18/07 12:08:53.
new patch, see notes on next comment.
ldapauth.1.diff (10.6 kB) - added by Scott Paul Robertson <spr@mahonri5.net> on 02/21/07 15:45:25.
Patch with one bug fix and some documentation improvements.
ldapauth.2.diff (10.6 kB) - added by Scott Paul Robertson <spr@mahonri5.net> on 04/10/07 08:32:50.
Fix a typo that is causing a bug in the prebind code.
ldapauthdoc.diff (2.9 kB) - added by programmerq on 12/30/07 13:52:07.
fixed a type from previous version
ldapauth.3.diff (11.0 kB) - added by bugs@erentil.net on 01/25/08 13:19:25.
changed random password generation, added is_active fields.
ldapauth.4.diff (12.7 kB) - added by guettli on 02/22/08 04:27:24.
See comment for ldapauth.4.diff
ldapauth.py (12.8 kB) - added by Rozza on 04/04/08 08:14:21.
See comments for ldapauth.py

Change History

08/09/06 15:53:11 changed by spr@mahonri5.net

  • attachment backends.py.diff added.

patch

08/10/06 13:32:49 changed by spr@mahonri5.net

  • attachment backends.py.2.diff added.

update to allow ldap options, and custom bind string functions. Also fixes a typo.

08/10/06 14:37:54 changed by spr@mahonri5.net

  • attachment backends.py.3.diff added.

Fixing bug with BIND_STRING_FUNC and adding a method to be used with BIND_STRING_FUNC.

08/11/06 11:48:56 changed by spr@mahonri5.net

  • attachment backends.py.4.diff added.

Slight update to make the users experience better with the pre_auth_bind.

08/11/06 14:45:07 changed by spr@mahonri5.net

  • attachment backends.py.5.diff added.

Better handling of searches, and setting good defaults to mk_pre_auth_bind()

08/12/06 15:13:46 changed by spr@mahonri5.net

  • attachment backends.py.6.diff added.

fixing use of LDAP_OPTIONS hash so code follows documentation

08/12/06 17:30:10 changed by spr@mahonri5.net

  • attachment ldap.diff added.

Random password, and adding defaults to global_settings.py to avoid loading errors.

08/14/06 23:21:48 changed by spr@mahonri5.net

  • attachment ldap.2.diff added.

Update to allow inheritance of LDAPBackend, so you can override the User object and generation of the bind string. This is the full patch for LDAP auth support.

08/15/06 17:10:34 changed by spr@mahonri5.net

  • attachment ldap.3.diff added.

Fixes a few run-time bugs, adding some more error checking.

08/16/06 22:19:17 changed by spr@mahonri5.net

  • attachment ldap.4.diff added.

Shouldn't have removed staticmethod from mk_pre_auth_bind.

09/22/06 08:31:10 changed by mtredinnick

Excuse my ignorance here, but how tightly bound is this to any particular LDAP backend? Should it work against more or less any implementation that is accessible via ldap://...?

I'm not sure yet if this should go into Django itself, but I'm leaning towards thinking it would be useful. The "pollution" (for want of a better word) of global_settings.py is a little unfortunate: we need a better way of having contrib/ applications providing their own setting defaults. Obviously not going to commit it right this minute, but it is being noticed.

11/06/06 15:58:22 changed by Scott Paul Robertson <spr@mahonri5.net>

It should work with any implementation that can be gotten to via ldap://. I've found references online that python-ldap works with Active Directory and eDirectory. It uses OpenLDAP's client libraries, which I've seen work with Apple's Open Directory as well.

I'm not happy either with the settings in global_settings.py, and would like to find a better solution.

FYI, I've been using this backend in production for about 3 months without issue.

02/08/07 06:53:33 changed by Marc Fargas <telenieko@telenieko.com>

for the record: I'm rewritting part of the patch so it has it's default values inside it without touching anything else so it can be a single .py file in contrib or downloaded by the user.

Also changing the _pre_bind() so it searches for the user dn: or makes it from a template (like it does now) so the user does not need to override anything, just setup some settings.

I'll let you know when it's done.

02/08/07 10:01:36 changed by Scott Paul Robertson <spr@mahonri5.net>

Marc:

I actually had been thinking about ways to get the settings in the file, just haven't had time to do anything about it.

Tweaking _pre_bind() sounds good.

Looking forward to see the changes.

02/17/07 10:16:36 changed by Simon G. <dev@simon.net.nz>

  • needs_better_patch set to 1.
  • stage changed from Unreviewed to Accepted.
  • needs_docs set to 1.

Marc: do you still have those modifications available? This would be a very nice feature to get into contrib.

02/17/07 15:26:24 changed by Marc <telenieko@telenieko.com>

Hi Simon, I'll try to attach my current patch this Monday (I'm out for the weekend), it currently handles authentication (tested against Active Directory) and getting user information. I'm working two more issues now, and I have one question for everybody interested.

  • Map arbitrary LDAP attributes to user.get_profile() fields.
  • Get group membership information (creating groups if needed)

The problem with getting group membership is that django does not support inheritance (a group being member of a group), while LDAP does. How would you handle this? Only get the first level memberships or get all the memberships and flatten them making the user be member of all the inherited groups directly?

Again, I'll try to upload the authentication part which "should word" at least with A.D. so you can put more comments this Monday and I'll work on those two issues during the week.

02/17/07 18:11:29 changed by Scott Paul Robertson <spr@mahonri5.net>

I'll provide OpenLDAP testing once the patch gets posted. If you're still fairly close to the original patch it should work just fine with OpenLDAP.

With user.get_profile(): I'm not familiar with the backend code for this. I'm guessing you're think of basically providing a dictionary straight from LDAP? If so I'd at least make it an optional feature.

Improving the group handling would be great. Considering the current patch just checks for staff or admin status it'd be nice to provide better control. Personally I'd go for flattening the groups.

We might want to bring this over to the dev list once the patch gets posted since other people aren't watching the ticket.

02/18/07 12:08:53 changed by Marc Fargas <telenieko@telenieko.com>

  • attachment ldapauth.diff added.

new patch, see notes on next comment.

02/18/07 12:21:24 changed by Marc Fargas <telenieko@telenieko.com>

Here's a new patch, two thing to do before applying:

  • Create a new directory: django/contrib/auth/contrib (that's, the last contrib is new)
  • touch/create a blank init.py file on this new directory

I think this is the best place to place "contributed authentication modules", documentation is still inside the code, there's one issue for documenting this (more below), the patch will create ldapauth.py and you should be able to use "django.contrib.auth.contrib.ldapauth.LDAPBackend" as an authentication backend and It should almost work as before.

The documentation issue is that djangoproject.com has no way to provide subdirectories (in this case /authentication/contributed/) which would place docs for this in an ugly url (/contributed_authenticators/ and /ldap_autenticator/), my idea was to create a new doc (/authentication/contributed/ or /contributed_authenticators/) to explain small contribued authenticators and link bigger ones like (/authentication/contributed/ldap/ or /ldap_authenticator/) just to be ready in case more authenticators get contributed. As you see URLs are far more clean with some directory depth support so I've left the docs where they are until we get this discussed ;)

The second thing left is the attributes mapping, right now the mapping is done "by hand" in _update_user my idea was to provide a setting with the ldap<>User mappings and loop over them:

    LDAP_ATTR_MAP = {'first_name': 'givenName', 'last_name': 'sn'}

That would make things far more customizable and a cleaner code, I talked on the get_profile() a few comments above, the idea would be to have a similar mapping directory for it being fully optional, but usefull if you have more attributes than the ones in User.

The third and last issue is group membership, until django support inheritance the cleanest approach is to flatten the memberships, On Active Directory I'll only have to take all "memberOf" attributes from the user and query all those cn's for more "memberOf" until I'm done, how is this done on OpenLDAP?

If this behaviour is much different we could ship a base LDAPBackend and an ADBackend and OLBackend which implement the different behaviours. I'll work with the mapping and the memberships this week as I need them.

Cheers, Marc.

(follow-up: ↓ 10 ) 02/21/07 15:44:36 changed by Scott Paul Robertson <spr@mahonri5.net>

Works out of the box with my OpenLDAP setup. I didn't need to change any settings.

I've attached a slight modification of the patch which changes:

  1. The except statement when we bind as the user to catch the case where someone doesn't enter their password.
  2. Minor adjustments to the internal documentation to clear up the wording and remove some now obsolete statements.

Also, why are the calls to the ldap module always done as self.ldap? It works either way, and removing the self would be cleaner code in my opinion.

Do we want to create the contrib directory? auth is already a contrib and it seems a bit excessive to have a contrib in contrib, but that just might be me.

It does seem that the documentation should be in a subdirectory. Maybe we can just write it up in the current authentication page for now.

I'm all for a LDAP_ATTR_MAP, it seems like a much cleaner method, and should be easy to maintain current functionality.

Group membership isn't the same between AD and OpenLDAP. OpenLDAP has group objects with have lists of users. We should go for a base LDAPBackend (which doesn't bother with groups), and subclass it for AD and OpenLDAP.

Looks good, thanks for the work, Scott

02/21/07 15:45:25 changed by Scott Paul Robertson <spr@mahonri5.net>

  • attachment ldapauth.1.diff added.

Patch with one bug fix and some documentation improvements.

(in reply to: ↑ 9 ; follow-up: ↓ 11 ) 02/21/07 16:07:32 changed by Marc Fargas <telenieko@telenieko.com>

Replying to Scott Paul Robertson <spr@mahonri5.net>:

Works out of the box with my OpenLDAP setup. I didn't need to change any settings.

Nice to know!!

Also, why are the calls to the ldap module always done as self.ldap? It works either way, and removing the self would be cleaner code in my opinion.

import ldap was inside the class when I took the patch, so I left it there. I think there would be no problem to move the import to the top of the file and then s/self.ldap/ldap/ ;)

Do we want to create the contrib directory? auth is already a contrib and it seems a bit excessive to have a contrib in contrib, but that just might be me.

Auth is in contrib, but "contributed authentication backends" seem as "contribs to auth", or "contribs to the auth contrib" hence the subdirectory.

It does seem that the documentation should be in a subdirectory. Maybe we can just write it up in the current authentication page for now.

It would mess the page a bit, we should try to do things fine from the start, sure somebody (willison?) will work on an OpenID backend and having the structure for those contribs (where to place the .py and the .txt) would be nice. And LDAP is a really speciffic matter, it should not go with the "generic" authentication documentation (I think).

I'm all for a LDAP_ATTR_MAP, it seems like a much cleaner method, and should be easy to maintain current functionality.

I'm a bit busy now but I'll go for it ASAP :)

Group membership isn't the same between AD and OpenLDAP. OpenLDAP has group objects with have lists of users. We should go for a base LDAPBackend (which doesn't bother with groups), and subclass it for AD and OpenLDAP.

AD has the "members" inside the group objects and users have "memberOf", in OpenLDAP how do you know which groups a user is member of? with a search?

Looks good, thanks for the work, Scott

you're welcome.

Maybe we can bring discussion to django-developers about where to place the docs and the .py itself :)

(in reply to: ↑ 10 ) 03/05/07 20:09:07 changed by Scott Paul Robertson <spr@mahonri5.net>

Replying to Marc Fargas <telenieko@telenieko.com>:

Replying to Scott Paul Robertson <spr@mahonri5.net>:

Also, why are the calls to the ldap module always done as self.ldap? It works either way, and removing the self would be cleaner code in my opinion.

import ldap was inside the class when I took the patch, so I left it there. I think there would be no problem to move the import to the top of the file and then s/self.ldap/ldap/ ;)

Sounds good to me.

Group membership isn't the same between AD and OpenLDAP. OpenLDAP has group objects with have lists of users. We should go for a base LDAPBackend (which doesn't bother with groups), and subclass it for AD and OpenLDAP.

AD has the "members" inside the group objects and users have "memberOf", in OpenLDAP how do you know which groups a user is member of? with a search?

Yeah, you do a search on a group sub-tree to find what groups a user is a member of. We have a lot of LDAP traffic in our labs. :)

Maybe we can bring discussion to django-developers about where to place the docs and the .py itself :)

I'll make a post tonight.

04/10/07 06:06:35 changed by korban

error in line 141 self.settings['LDAP_PREVINDPW'])

04/10/07 08:32:50 changed by Scott Paul Robertson <spr@mahonri5.net>

  • attachment ldapauth.2.diff added.

Fix a typo that is causing a bug in the prebind code.

09/13/07 02:58:25 changed by Wang Chun <wangchun@exoweb.net>

  • cc set to wangchun@exoweb.net.

12/29/07 18:50:58 changed by programmerq

  • version set to SVN.

I added the patch with documentation. I am sure it needs improvement, but I'd like to see the ldap backend get merged into trunk, so I wrote up some documentation. Some of it comes from comments in the source code. This patch is against svn 6980 (current as of now)

12/30/07 13:52:07 changed by programmerq

  • attachment ldapauthdoc.diff added.

fixed a type from previous version

01/19/08 21:54:57 changed by programmerq

  • needs_docs deleted.

I wrote the documentation for it. I forgot to mark it as being such when I uploaded the patch, so I'm marking it now.

01/22/08 02:51:58 changed by guettli

  • cc changed from wangchun@exoweb.net to wangchun@exoweb.net, hv@tbz-pariv.de.

01/25/08 09:28:21 changed by anonymous

  • cc changed from wangchun@exoweb.net, hv@tbz-pariv.de to wangchun@exoweb.net, hv@tbz-pariv.de, cmawebsite@gmail.com.

01/25/08 13:19:25 changed by bugs@erentil.net

  • attachment ldapauth.3.diff added.

changed random password generation, added is_active fields.

02/16/08 20:26:51 changed by christoph.neuroth@gmx.net

Thanks, works great for me :) however it breaks the "change password" feature in the admin utility...

and something else I noted: the code does try to use StartTLS over ldap:// urls. after the .initialize just do something like this:

if LDAP_SERVER_URI.startswith('ldap:'):
  try:
    l.start_tls_s()
  except FindOutWhatExceptionItwouldRaiseIfStartTLSWasNotSupported ;):
    pass

sorry that I don't write a proper patch but I'm just too tired right now ;)

02/22/08 04:27:24 changed by guettli

  • attachment ldapauth.4.diff added.

See comment for ldapauth.4.diff

02/22/08 04:31:57 changed by guettli

Comment for ldapauth.4.diff:

  • LDAP_DEBUG was added. If True, the logging module is used for debug messages.
  • authenticate(): if _pre_bind returns nothing, stop and return None.
  • If LDAP_ACTIVE_FIELD is not set, all users are active.

03/03/08 10:13:57 changed by Christoph Neuroth <christoph.neuroth@gmx.net>

Another feature would be nice: While sometimes you can group your users by simply using an "ou" entry in the dn, often you have to use a more complex group structure, like this:

cn:groupname,ou=Groups,dc=examplefoo
objectClass:posixGroup
memberUid:foo
memberUid:bar
memberUid:baz

or like this:

cn:groupname2,ou=Groups,dc=examplefoo
objectClass:groupOfPeople
member: uid=foo,ou=Users,dc=examplefoo
member: uid=bar,ou=Users,dc=examplefoo
member: uid=baz,ou=Users,dc=examplefoo

I would like to use the first one to determine which users are active/staff/admin but I can't find a way to do this right now without patching the authentification module...

04/04/08 08:11:47 changed by Rozza

Comment for file: ldapauth.py

  1. Fixes issue losing ldap connect when using LDAP_PREBINDDN and LDAP_PREBINDPW for systems that don't allow anonymous ldap lookups
  2. Fixes logic for checking GID's for superusers and staff.
    • Now can pass an identifier to see if the user belongs to and if the user is in the matching group then they pass i.e.:
      'LDAP_GID': 'memberOf',
      'LDAP_SU_GIDS': ['CN=systemadmins,OU=Headoffice,DC=example,DC=com'],
      'LDAP_STAFF_GIDS': ['CN=allstaff,,OU=Headoffice,DC=example,DC=com'],
          
      
      # User x has the following values for memberOf:  ['CN=systemadmins,OU=Headoffice,DC=example,DC=com', 'CN=allstaff,OU=Headoffice,DC=example,DC=com']
      # Will have the following user attributes: user.is_super = True user.is_staff = True 
      # User y who has the following values for memberOf:  ['CN=admin,OU=Headoffice,DC=example,DC=com', 'CN=allstaff,OU=Headoffice,DC=example,DC=com']
      # Will have the following user attributes: user.is_super = False user.is_staff = True 
      # User z who has the following values for memberOf:  ['CN=tmpstaff,OU=Headoffice,DC=example,DC=com']
      # Will have the following user attributes: user.is_super = False user.is_staff = False 
      
  3. Fixes logic for isactive if not set

04/04/08 08:14:21 changed by Rozza

  • attachment ldapauth.py added.

See comments for ldapauth.py

04/21/08 10:51:18 changed by Christoph Neuroth <christoph.neuroth@gmx.net>

self.settings.LDAP_ACTIVE* should be self.settingsLDAP_ACTIVE*?, or am I wrong? current ldapauth.py throws exception because of this. greets, c

04/21/08 10:52:00 changed by Christoph Neuroth <christoph.neuroth@gmx.net>

I meant this:

self.settings['LDAP_ACTIVE*']

Add/Change #2507 ([patch] LDAPBackend in django/contrib/auth/backends.py)




Change Properties
Action