Index: django/contrib/auth/backends.py
===================================================================
--- django/contrib/auth/backends.py	(revision 3545)
+++ django/contrib/auth/backends.py	(working copy)
@@ -1,4 +1,5 @@
 from django.contrib.auth.models import User
+from django.conf import settings
 
 class ModelBackend:
     """
@@ -19,3 +20,151 @@
             return User.objects.get(pk=user_id)
         except User.DoesNotExist:
             return None
+
+class LDAPBackend(object):
+    """
+    Authenticate a user against LDAP.
+    Requires python-ldap to be installed.
+
+    Requires the following things to be in settings.py:
+    LDAP_BINDDN -- string of the LDAP dn to use for binding
+    LDAP_SEARCHDN -- string of the LDAP dn to use for searching
+    LDAP_BIND_ATTRIBUTE -- string of the LDAP attribute to use in binding
+        also used to search for a user.
+    LDAP_SERVER_URI -- string, ldap uri
+    LDAP_SCOPE -- one of: ldap.SCOPE_*, used for searching
+        ldap.SCOPE_BASE = 0
+        ldap.SCOPE_ONELEVEL = 1
+        ldap.SCOPE_SUBTREE = 2
+        see python-ldap docs for the search function
+    LDAP_UPDATE_FIELDS -- boolean, do we sync the db with ldap with each auth
+
+    Required unless LDAP_FULL_NAME is set:
+    LDAP_FIRST_NAME -- string, LDAP attribute to get the given name from
+    LDAP_LAST_NAME -- string, LDAP attribute to get the last name from
+
+    Optional Settings:
+    LDAP_FULL_NAME -- string, LDAP attribute to get name from, splits on ' '
+    LDAP_GID -- string, LDAP attribute to get group name/number from
+    LDAP_SU_GIDS -- list of strings, group names/numbers that are superusers
+    LDAP_STAFF_GIDS -- list of strings, group names/numbers that are staff
+    LDAP_EMAIL -- string, LDAP attribute to get email from
+    LDAP_DEFAULT_EMAIL_SUFFIX -- string, appened to username if no email found
+    LDAP_OPTIONS -- hash, python-ldap global options and their values
+        {ldap.OPT_X_TLS_CACERTDIR: '/etc/ldap/ca/'}
+    LDAP_BIND_STRING_FUNC -- Function to produce the string for binding the user
+
+    How Binds Work:
+    LDAP_BINDDN = 'ou=people,dc=example,dc=com'
+    LDAP_BIND_ATTRIBUTE = 'uid'
+    # The bind would be performed via:
+    # uid=username,ou=people,dc=example,dc=com
+    """ 
+    import ldap
+
+    def authenticate(self, username=None, password=None):
+        if not username and password is not None: # we need a user/pass
+            l.unbind_s()
+            return None
+
+        if hasattr(settings, 'LDAP_OPTIONS'):
+            for k, v in settings.LDAP_OPTIONS:
+                ldap.set_option(k, v)
+
+        l = ldap.initialize(settings.LDAP_SERVER_URI)
+
+        if hasattr(settings, 'LDAP_BIND_STRING_FUNC'):
+            bind_string = settings.LDAP_BIND_STRING_FUNC
+        else:
+            bind_string = "%s=%s,%s" % (settings.LDAP_BIND_ATTRIBUTE,
+                    username, settings.LDAP_BINDDN)
+        try:
+            l.bind_s(bind_string, password)
+        except ldap.INVALID_CREDENTIALS: # Failed user/pass
+            l.unbind_s()
+            return None
+
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist:
+            user = None
+
+        if user is not None:
+            if settings.LDAP_UPDATE_FIELDS:
+                LDAPBackend.update_user(l, user)
+        else:
+            user = LDAPBackend.get_ldap_user(l, username)
+
+        l.unbind_s()
+        return user
+
+    def get_user(self, user_id):
+        try:
+            return User.objects.get(pk=user_id)
+        except:
+            return None
+
+    def get_ldap_user(l, username):
+        """
+        Helper method, makes a user object and call update_user to populate
+        """
+
+        user = User(username=username, password='Made by LDAP')
+        LDAPBackend.update_user(l, user)
+        return user
+    get_ldap_user = staticmethod(get_ldap_user)
+
+    def update_user(l, user):
+        """
+        Helper method, populates a user object with various attributes from
+        LDAP
+        """
+
+        username = user.username
+        filter_str = "%s=%s" % (settings.LDAP_BIND_ATTRIBUTE, username)
+        attrs = l.search_s(settings.LDAP_SEARCHDN, settings.LDAP_SCOPE,
+                filterstr=filter_str)[0][1]
+
+        if (hasattr(settings, 'LDAP_FIRST_NAME')
+                and hasattr(settings, 'LDAP_LAST_NAME')):
+            if (settings.LDAP_FIRST_NAME in attrs
+                    and settings.LDAP_LAST_NAME in attrs):
+                user.first_name = attrs[settings.LDAP_FIRST_NAME][0]
+                user.last_name = attrs[settings.LDAP_LAST_NAME][0]
+            else:
+                raise NameError('Missing needed fields %s or %s in LDAP'
+                        % (settings.LDAP_FIRST_NAME, settings.LDAP_LAST_NAME))
+        elif hasattr(settings, 'LDAP_FULL_NAME'):
+                if settings.LDAP_FULL_NAME in attrs:
+                    tmp = attrs[settings.FULL_NAME_FIELD][0]
+                    user.first_name = tmp.split(' ')[0]
+                    user.last_name = ' '.join(tmp.split(' ')[1:])
+                else:
+                    raise NameError('Required field %s missing in LDAP'
+                            % (settings.LDAP_FULL_NAME))
+        else:
+            raise NameError('Name fields not defined in settings.py')
+
+        if hasattr(settings, 'LDAP_EMAIL') and settings.LDAP_EMAIL in attrs:
+            user.email = attrs[settings.EMAIL_FIELD][0]
+        elif hasattr(settings, 'LDAP_DEFAULT_EMAIL_SUFFIX'):
+            user.email = username + settings.LDAP_DEFAULT_EMAIL_SUFFIX
+
+        if (hasattr(settings, 'LDAP_GID')
+                and settings.LDAP_GID in attrs 
+                and hasattr(settings, 'LDAP_SU_GIDS')
+                and attrs[settings.LDAP_GID][0] in settings.LDAP_SU_GIDS):
+            user.is_superuser = True
+            user.is_staff = True
+        elif (hasattr(settings, 'LDAP_GID')
+                and settings.LDAP_GID in attrs 
+                and hasattr(settings, 'LDAP_STAFF_GIDS')
+                and attrs[settings.LDAP_GID][0] in settings.LDAP_STAFF_GIDS):
+            user.is_superuser = False
+            user.is_staff = True
+        else:
+            user.is_superuser = False
+            user.is_staff = False
+
+        user.save()
+    update_user = staticmethod(update_user)
