Skip to content

Instantly share code, notes, and snippets.

@maxicecilia
Created June 2, 2015 19:40
Show Gist options
  • Save maxicecilia/ab3bb3dcf697f15554e6 to your computer and use it in GitHub Desktop.
Save maxicecilia/ab3bb3dcf697f15554e6 to your computer and use it in GitHub Desktop.
LDAP BE
# This was modified from the original version posted here:
#
# http://www.carthage.edu/webdev/?p=12
#
# ... retrieved around 1/12/2008.
#
# Modifications include:
#
# 1. Using a simple bind for auth instead of a search
# 2. Upping the default privileges for users
# 3. Modifying the required "constants" and moving them into settings.py
# 4. Extending ModelBackend "just in case"
# 5. Verifying that the username is all word chars before passing
# it off to the LDAP API
import ldap, re, logging
from django.conf import settings
from django.contrib.auth.models import User
import django.contrib.auth.backends
# Constants
#
# Load them from settings if you can. Error if you can't.
# Sample values are provided for reference.
# AUTH_LDAP_SERVER = "localhost"
# AUTH_LDAP_USER_DN_FORMAT = "uid=%s, ou=people, dc=nationalgeographic, dc=com"
AUTH_LDAP_SERVER = settings.AUTH_LDAP_SERVER
AUTH_LDAP_BACKUP_SERVER = settings.AUTH_LDAP_BACKUP_SERVER
AUTH_LDAP_USER_DN_FORMAT = settings.AUTH_LDAP_USER_DN_FORMAT
AUTH_LDAP_SUPERUSER_BY_DEFAULT = settings.AUTH_LDAP_SUPERUSER_BY_DEFAULT
class LDAPBackend(django.contrib.auth.backends.ModelBackend):
def authenticate(self, username=None, password=None):
# Disallow anonymous logins by require a non-empty password.
#
# Experimental testing shows that non-empty, all whitespace
# passwords fail as expected.
if password == "":
return None
# Do some validation on the username... it should
# be all word chars.
#
# TODO Does django already sanitize this for us?
if re.compile("^[-_\w\d]+$").match(username) == None:
logging.error("Username isn't valid (not a combination of -, _, numbers and letters)")
return None
try:
l = ldap.open(AUTH_LDAP_SERVER)
except ldap.CONNECT_ERROR, e:
logging.warning("Connect to main ldap server (%s) failed." % (AUTH_LDAP_SERVER))
try:
l = ldap.open(AUTH_LDAP_BACKUP_SERVER)
except ldap.CONNECT_ERROR, e:
logging.warning("Connect to backup ldap server (%s) failed." % (AUTH_LDAP_BACKUP_SERVER))
return None
# Authenticate the base user so we can search
try:
dn = AUTH_LDAP_USER_DN_FORMAT % username
logging.debug("Trying to bind to ldap server as %s" % (dn))
l.protocol_version = ldap.VERSION3
l.simple_bind_s(dn, password)
# If we error, we return None and fail to authenticate.
except ldap.INVALID_CREDENTIALS:
# Name or password were bad. Fail.
return None
except ldap.LDAPError, e:
logging.error("Got an LDAP error on binding %s: %s" % (username, e))
return None
# Now use search to get the user's email address
try:
baseDN = "ou=people,dc=nationalgeographic,dc=com"
filter = 'uid=%s' % username
attrs = ["uid", "mail"]
records = l.search_s(baseDN, ldap.SCOPE_SUBTREE, filter, attrs)
except:
return None
if len(records) != 1:
# search failed
return None
uid_string, uid_attrs = records[0]
# logging.debug("Extracting LDAP record: %s, %s" % (uid_string, uid_attrs))
if 'mail' in uid_attrs:
email_addr = uid_attrs['mail'][0]
else:
email_addr = ''
l.unbind()
### logging.debug( "Trying to fetch or create user..." )
try:
# The user existed and authenticated.
# Get the user record or else create one.
#
# Users created here will always need to provide their LDAP
# password. Invoking set_unusable_password() ensures that.
#
# Note that since the user isn't written to the db
# until after we set the unusable password, there's
# no race condition here where a malicious visitor
# could sneak in.
user = User.objects.get(username__exact=username)
except:
user = User.objects.create_user(username, email_addr, "stub password")
user.set_unusable_password()
# Due to our policy of trusted users, we currently default
# new accounts to staff if they are in LDAP.
#
# This is will eventually be replaced with a permissions
# scheme where ldap attribs will control application permissions.
user.is_staff = True
if AUTH_LDAP_SUPERUSER_BY_DEFAULT:
user.is_superuser = True
user.save()
# Success.
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment