Kaarsemaker.net


auth_ldap.py
6.24 KB

144 lines

Download

sqlviews.py
changes.py

Preview

 1 # Ldap authentication backend for Django. Users are authenticated against
 2 # an ldap server and their django accounts (name, email) are maintained
 3 # automatically.
 4 #
 5 # Configuration is done in settings.py, these are the available settings:
 6 #
 7 # # Where to find the ldap server (optional, default: ldap://localhost)
 8 # AUTH_LDAP_HOST = 'ldap://ldap.kaarsemaker.net'
 9 # # Which ldap groups to mirror in django (optional, default [])
10 # AUTH_LDAP_GROUPS = ('webadmins','ubuntu')
11 # # The users must be in any of these groups (optional, default [])
12 # AUTH_LDAP_FILTER_GROUPS = AUTH_LDAP_GROUPS
13 # # DN for binding to the server (optional, default anonymous bind)
14 # #AUTH_LDAP_BINDDN = "cn=admin,dc=kaarsemaker,dc=net"
15 # #AUTH_LDAP_BINDPW = "TheAdminPassword
  1 # Ldap authentication backend for Django. Users are authenticated against
  2 # an ldap server and their django accounts (name, email) are maintained
  3 # automatically.
  4 #
  5 # Configuration is done in settings.py, these are the available settings:
  6 #
  7 # # Where to find the ldap server (optional, default: ldap://localhost)
  8 # AUTH_LDAP_HOST = 'ldap://ldap.kaarsemaker.net'
  9 # # Which ldap groups to mirror in django (optional, default [])
 10 # AUTH_LDAP_GROUPS = ('webadmins','ubuntu')
 11 # # The users must be in any of these groups (optional, default [])
 12 # AUTH_LDAP_FILTER_GROUPS = AUTH_LDAP_GROUPS
 13 # # DN for binding to the server (optional, default anonymous bind)
 14 # #AUTH_LDAP_BINDDN = "cn=admin,dc=kaarsemaker,dc=net"
 15 # #AUTH_LDAP_BINDPW = "TheAdminPassword
 16 # # Base DN for users and groups (required)
 17 # AUTH_LDAP_BASEDN_USER = 'ou=People,dc=kaarsemaker,dc=net'
 18 # AUTH_LDAP_BASEDN_GROUP = 'ou=Group,dc=kaarsemaker,dc=net'
 19 # # Do we need to make the user staff?
 20 # AUTH_LDAP_CREATE_STAFF = True
 21 #
 22 # # If you want LDAP to be your only authentication source, use
 23 # AUTHENTICATION_BACKENDS = ('myproject.auth_ldap.LdapAuthBackend',)
 24 # # If you want to use ldap and fall back to django, use
 25 # AUTHENTICATION_BACKENDS = ('myproject.auth_ldap.LdapAuthBackend',
 26 #                            'django.contrib.auth.backends.ModelBackend')
 27 #
 28 # When using ldap exclusively, the superuser created with ./manage.py 
 29 # cannot log in unless the account also exists in ldap. So either make
 30 # sure the user exists in ldap or give another user superuser rights
 31 # before disabling the builtin authentication.
 32 #
 33 # Make sure all your ldap users have a mail attribute, otherwise this
 34 # module will break.
 35 #
 36 # If you use django with mod_python, please make sure no other apache module
 37 # drags in a different version of libldap than what python wants to link to
 38 # or you will see protocol errors. If a ./manage.py runserver instance works
 39 # but apache not, try switching to mod_fcgi, which runs the django code in a
 40 # separate process.
 41 #
 42 # Copyright (c) 2008,2009 Dennis Kaarsemaker <dennis@kaarsemaker.net>
 43 # All rights reserved.
 44 # 
 45 # Redistribution and use in source and binary forms, with or without modification,
 46 # are permitted provided that the following conditions are met:
 47 # 
 48 #     1. Redistributions of source code must retain the above copyright notice, 
 49 #        this list of conditions and the following disclaimer.
 50 #     
 51 #     2. Redistributions in binary form must reproduce the above copyright 
 52 #        notice, this list of conditions and the following disclaimer in the
 53 #        documentation and/or other materials provided with the distribution.
 54 # 
 55 #     3. Neither the name of Django nor the names of its contributors may be used
 56 #        to endorse or promote products derived from this software without
 57 #        specific prior written permission.
 58 # 
 59 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 60 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 61 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 62 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 63 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 64 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 65 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 66 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 67 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 68 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 69 
 70 from django.contrib.auth.models import User, Group
 71 from django.contrib.auth.backends import ModelBackend
 72 from django.conf import settings
 73 import ldap
 74 
 75 def _find_dn(ls, username):
 76     ls.bind_s(getattr(settings, 'AUTH_LDAP_BINDDN', ''),
 77               getattr(settings, 'AUTH_LDAP_BINDPW', ''))
 78     res = ls.search_s(settings.AUTH_LDAP_BASEDN_USER, ldap.SCOPE_ONELEVEL,
 79                       "uid=" + username, [])
 80     if not len(res):
 81         return
 82     return res[0]
 83 
 84 def _find_groups(ls, username):
 85     if not getattr(settings, 'AUTH_LDAP_GROUPS', None) and \
 86        not getattr(settings, 'AUTH_LDAP_FILTER_GROUPS', None):
 87         return []
 88     ls.bind_s(getattr(settings, 'AUTH_LDAP_BINDDN', ''),
 89               getattr(settings, 'AUTH_LDAP_BINDPW', ''))
 90     res = ls.search_s(settings.AUTH_LDAP_BASEDN_GROUP, ldap.SCOPE_ONELEVEL,
 91                       "memberUid=" + username, [])
 92     return [x[1]['cn'][0] for x in res]
 93 
 94 class LdapAuthBackend(ModelBackend):
 95     def authenticate(self, username=None, password=None):
 96         # Authenticate against ldap
 97         ls = ldap.initialize(getattr(settings, 'AUTH_LDAP_HOST', 'ldap://localhost'))
 98         dn, attrs = _find_dn(ls, username)
 99         if not dn:
100             ls.unbind()
101             return
102         try:
103             ls.bind_s(dn, password)
104         except ldap.INVALID_CREDENTIALS:
105             ls.unbind()
106             return
107 
108         # Are we allowed to log in
109         groups = _find_groups(ls, username)
110         if getattr(settings, 'AUTH_LDAP_FILTER_GROUPS', None):
111             for group in getattr(settings,'AUTH_LDAP_FILTER_GROUPS',[]):
112                 if group in groups:
113                     break
114             else:
115                 ls.unbind()
116                 return
117 
118         # OK, we've authenticated. Do we exist?
119         try:
120             user = User.objects.get(username=username)
121         except User.DoesNotExist:
122             user = User.objects.create_user(username, attrs['mail'][0], password)
123             user.is_active = True
124             if getattr(settings, 'AUTH_LDAP_CREATE_STAFF', False))
125                 user.is_staff = True
126         user.first_name = attrs['givenName'][0]
127         user.last_name = attrs['sn'][0]
128         user.email = attrs['mail'][0]
129         user.password = 'This is an LDAP account'
130 
131         # Group manglement
132         for group in getattr(settings,'AUTH_LDAP_GROUPS',[]):
133             dgroup, created = Group.objects.get_or_create(name=group)
134             if created:
135                 dgroup.save()
136             if dgroup in user.groups.all() and group not in groups:
137                 user.groups.remove(dgroup)
138             if dgroup not in user.groups.all() and group in groups:
139                 user.groups.add(dgroup)
140 
141         # Done!
142         user.save()
143         ls.unbind()
144         return user

Show all