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