I had some trouble getting other peoples code to work for AD support, so I wrote my own which authenticates against LDAP and will also use SSL and cert if required. It will also verify that an autheticated user has specific group membership before authorizing.
This will also debug to a file, which is really helpful when trying to figure out problems.
One thing that really got me when getting python-ldap to work was that you must have "ldap.set_option(ldap.OPT_REFERRALS,0)" set or any ldap search will not work.
Also, this will add group permissions to a user.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | In settings:
AD_DNS_NAME='your-ldap-server.com'
# If using non-SSL use these
#AD_LDAP_PORT=389
#AD_LDAP_URL='ldap://%s:%s' %
(AD_DNS_NAME,AD_LDAP_PORT)
# If using SSL use these:
AD_LDAP_PORT=636
AD_LDAP_URL='ldaps://%s:%s' % (AD_DNS_NAME,AD_LDAP_PORT)
AD_SEARCH_DN='dc=mygroup,dc=net,dc=com'
AD_NT4_DOMAIN='YOURDOMAIN'
AD_SEARCH_FIELDS= ['mail','givenName','sn','sAMAccountName','memberOf']
AD_MEMBERSHIP_REQ=['Group_Required','Alternative_Group']
AD_CERT_FILE='/path/to/your/cert.txt'
AUTHENTICATION_BACKENDS = ('reviewboard.accounts.backends.ActiveDirectoryGroupMembershipSSLBackend',
'django.contrib.auth.backends.ModelBackend')
AD_DEBUG=True
AD_DEBUG_FILE='/path/to/writable/log/file/ldap.debug'
In accounts/backends.py:
class ActiveDirectoryGroupMembershipSSLBackend:
def authenticate(self,username=None,password=None):
try:
if len(password) == 0:
return None
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,settings.AD_CERT_FILE)
l = ldap.initialize(settings.AD_LDAP_URL)
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
binddn = "%s@%s" % (username,settings.AD_NT4_DOMAIN)
l.simple_bind_s(binddn,password)
l.unbind_s()
return self.get_or_create_user(username,password)
except ImportError:
pass
except ldap.INVALID_CREDENTIALS:
pass
def get_or_create_user(self, username, password):
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
try:
# debug info
debug=0
if len(settings.AD_DEBUG_FILE) > 0:
if settings.AD_DEBUG:
debug=open(settings.AD_DEBUG_FILE,'w')
print >>debug, "create user %s" % username
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,settings.AD_CERT_FILE)
ldap.set_option(ldap.OPT_REFERRALS,0) # DO NOT TURN THIS OFF OR SEARCH WON'T WORK!
# initialize
if debug:
print >>debug, 'ldap.initialize...'
l = ldap.initialize(settings.AD_LDAP_URL)
l.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
# bind
if debug:
print >>debug, 'bind...'
binddn = "%s@%s" % (username,settings.AD_NT4_DOMAIN)
l.bind_s(binddn,password)
# search
if debug:
print >>debug, 'search...'
result = l.search_ext_s(settings.AD_SEARCH_DN,ldap.SCOPE_SUBTREE,"sAMAccountName=%s" % username,settings.AD_SEARCH_FIELDS)[0][1]
if debug:
print >>debug, result
# Validate that they are a member of review board group
if result.has_key('memberOf'):
membership = result['memberOf']
else:
membership = None
if debug:
print >>debug, "required:%s" % settings.AD_MEMBERSHIP_REQ
bValid=0
for req_group in settings.AD_MEMBERSHIP_REQ:
if debug:
print >>debug, "Check for %s group..." % req_group
for group in membership:
group_str="CN=%s," % req_group
if group.find(group_str) >= 0:
if debug:
print >>debug, "User authorized: group_str membership found!"
bValid=1
break
if bValid == 0:
if debug:
print >>debug, "User not authorized, correct group membership not found!"
return None
# get email
if result.has_key('mail'):
mail = result['mail'][0]
else:
mail = None
if debug:
print >>debug, "mail=%s" % mail
# get surname
if result.has_key('sn'):
last_name = result['sn'][0]
else:
last_name = None
if debug:
print >>debug, "sn=%s" % last_name
# get display name
if result.has_key('givenName'):
first_name = result['givenName'][0]
else:
first_name = None
if debug:
print >>debug, "first_name=%s" % first_name
l.unbind_s()
user = User(username=username,first_name=first_name,last_name=last_name,email=mail)
except Exception, e:
if debug:
print >>debug, "exception caught!"
print >>debug, e
return None
user.is_staff = False
user.is_superuser = False
user.set_password('ldap authenticated')
user.save()
# add user to default group
group=Group.objects.get(pk=1)
if debug:
print >>debug, group
if debug:
print >>debug, "add %s to group %s" % (username,group)
user.groups.add(group)
user.save()
if debug:
print >>debug, "successful group add"
if debug:
debug.close()
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Two questions about the certificate you used:
#
Just to note, this snippet is very useful, but you really need to make sure that you have the groups in your django db. Otherwise you will see errors like:
So, I patched the script to catch these errors with:
#
Beware to change line 133
user.set_password('ldap authenticated')
to a random number like:Otherwise, anyone can log in as a user with password "ldap authenticated".
#
Please login first before commenting.