Commits

Peter Sagerson committed fa96b29

Support multiple LDAP configs with LDAPBackend.settings_prefix.

  • Participants
  • Parent commits dab2965

Comments (0)

Files changed (4)

File django_auth_ldap/backend.py

 
     ldap = None # The cached ldap module (or mock object)
 
+    # This is prepended to our internal setting names to produce the names we
+    # expect in Django's settings file. Subclasses can change this in order to
+    # support multiple collections of settings.
+    settings_prefix = 'AUTH_LDAP_'
+
     def __init__(self):
+        self.settings = LDAPSettings(self.settings_prefix)
         self.ldap = self.ldap_module()
 
-    def ldap_module(cls):
+    def ldap_module(self):
         """
         Requests the ldap module from _LDAPConfig. Under a test harness, this
-        will be a mock object. We only do this once because this is where we
-        apply AUTH_LDAP_GLOBAL_OPTIONS.
+        will be a mock object.
         """
-        if cls.ldap is None:
-            cls.ldap = _LDAPConfig.get_ldap()
+        from django.conf import settings
 
-            for opt, value in ldap_settings.AUTH_LDAP_GLOBAL_OPTIONS.iteritems():
-                cls.ldap.set_option(opt, value)
+        options = getattr(settings, 'AUTH_LDAP_GLOBAL_OPTIONS', None)
 
-        return cls.ldap
-    ldap_module = classmethod(ldap_module)
+        return _LDAPConfig.get_ldap(options)
 
 
     #
         return self.get_group_permissions(user, obj)
 
     def get_group_permissions(self, user, obj=None):
-        if not hasattr(user, 'ldap_user') and ldap_settings.AUTH_LDAP_AUTHORIZE_ALL_USERS:
+        if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS:
             _LDAPUser(self, user=user) # This sets user.ldap_user
 
         if hasattr(user, 'ldap_user'):
         ignored.
         """
         self.backend = backend
-        self.ldap = backend.ldap_module()
+        self.ldap = backend.ldap
+        self.settings = backend.settings
         self._username = username
         self._user_dn = None
         self._user_attrs = None
         if self._group_permissions is None:
             self._group_permissions = set()
 
-            if ldap_settings.AUTH_LDAP_FIND_GROUP_PERMS:
+            if self.settings.FIND_GROUP_PERMS:
                 try:
                     self._load_group_permissions()
                 except self.ldap.LDAPError, e:
             raise self.AuthenticationFailed("Failed to map the username to a DN.")
 
         try:
-            sticky = ldap_settings.AUTH_LDAP_BIND_AS_AUTHENTICATING_USER
+            sticky = self.settings.BIND_AS_AUTHENTICATING_USER
 
             self._bind_as(self.dn, password, sticky=sticky)
         except self.ldap.INVALID_CREDENTIALS:
             self._search_for_user_dn()
 
     def _using_simple_bind_mode(self):
-        return (ldap_settings.AUTH_LDAP_USER_DN_TEMPLATE is not None)
+        return (self.settings.USER_DN_TEMPLATE is not None)
 
     def _construct_simple_user_dn(self):
-        template = ldap_settings.AUTH_LDAP_USER_DN_TEMPLATE
+        template = self.settings.USER_DN_TEMPLATE
         username = self.ldap.dn.escape_dn_chars(self._username)
 
         self._user_dn = template % {'user': username}
         Searches the directory for a user matching AUTH_LDAP_USER_SEARCH.
         Populates self._user_dn and self._user_attrs.
         """
-        search = ldap_settings.AUTH_LDAP_USER_SEARCH
+        search = self.settings.USER_SEARCH
         if search is None:
             raise ImproperlyConfigured('AUTH_LDAP_USER_SEARCH must be an LDAPSearch instance.')
 
         Returns True if the group requirement (AUTH_LDAP_REQUIRE_GROUP) is
         met. Always returns True if AUTH_LDAP_REQUIRE_GROUP is None.
         """
-        required_group_dn = ldap_settings.AUTH_LDAP_REQUIRE_GROUP
+        required_group_dn = self.settings.REQUIRE_GROUP
 
         if required_group_dn is not None:
             is_member = self._get_groups().is_member_of(required_group_dn)
         Returns True if the negative group requirement (AUTH_LDAP_DENY_GROUP)
         is met. Always returns True if AUTH_LDAP_DENY_GROUP is None.
         """
-        denied_group_dn = ldap_settings.AUTH_LDAP_DENY_GROUP
+        denied_group_dn = self.settings.DENY_GROUP
 
         if denied_group_dn is not None:
             is_member = self._get_groups().is_member_of(denied_group_dn)
         self._user.ldap_user = self
         self._user.ldap_username = self._username
 
-        should_populate = force_populate or ldap_settings.AUTH_LDAP_ALWAYS_UPDATE_USER or created
+        should_populate = force_populate or self.settings.ALWAYS_UPDATE_USER or created
 
         if created:
             logger.debug("Created Django user %s", username)
             self._populate_user()
             save_user = True
 
-        if ldap_settings.AUTH_LDAP_MIRROR_GROUPS:
+        if self.settings.MIRROR_GROUPS:
             self._mirror_groups()
 
         # Give the client a chance to finish populating the user just before
         self._populate_user_from_group_memberships()
 
     def _populate_user_from_attributes(self):
-        for field, attr in ldap_settings.AUTH_LDAP_USER_ATTR_MAP.iteritems():
+        for field, attr in self.settings.USER_ATTR_MAP.iteritems():
             try:
                 setattr(self._user, field, self.attrs[attr][0])
             except StandardError:
                 logger.warning("%s does not have a value for the attribute %s", self.dn, attr)
 
     def _populate_user_from_group_memberships(self):
-        for field, group_dn in ldap_settings.AUTH_LDAP_USER_FLAGS_BY_GROUP.iteritems():
+        for field, group_dn in self.settings.USER_FLAGS_BY_GROUP.iteritems():
             value = self._get_groups().is_member_of(group_dn)
             setattr(self._user, field, value)
 
         """
         save_profile = False
 
-        for field, attr in ldap_settings.AUTH_LDAP_PROFILE_ATTR_MAP.iteritems():
+        for field, attr in self.settings.PROFILE_ATTR_MAP.iteritems():
             try:
                 # user_attrs is a hash of lists of attribute values
                 setattr(profile, field, self.attrs[attr][0])
         """
         save_profile = False
 
-        for field, group_dn in ldap_settings.AUTH_LDAP_PROFILE_FLAGS_BY_GROUP.iteritems():
+        for field, group_dn in self.settings.PROFILE_FLAGS_BY_GROUP.iteritems():
             value = self._get_groups().is_member_of(group_dn)
             setattr(profile, field, value)
             save_profile = True
         Binds to the LDAP server with AUTH_LDAP_BIND_DN and
         AUTH_LDAP_BIND_PASSWORD.
         """
-        self._bind_as(ldap_settings.AUTH_LDAP_BIND_DN,
-            ldap_settings.AUTH_LDAP_BIND_PASSWORD,
+        self._bind_as(self.settings.BIND_DN,
+            self.settings.BIND_PASSWORD,
             sticky=True)
 
     def _bind_as(self, bind_dn, bind_password, sticky=False):
         Returns our cached LDAPObject, which may or may not be bound.
         """
         if self._connection is None:
-            self._connection = self.ldap.initialize(ldap_settings.AUTH_LDAP_SERVER_URI)
+            self._connection = self.ldap.initialize(self.settings.SERVER_URI)
 
-            for opt, value in ldap_settings.AUTH_LDAP_CONNECTION_OPTIONS.iteritems():
+            for opt, value in self.settings.CONNECTION_OPTIONS.iteritems():
                 self._connection.set_option(opt, value)
 
-            if ldap_settings.AUTH_LDAP_START_TLS:
+            if self.settings.START_TLS:
                 logger.debug("Initiating TLS")
                 self._connection.start_tls_s()
 
     Represents the set of groups that a user belongs to.
     """
     def __init__(self, ldap_user):
+        self.settings = ldap_user.settings
         self._ldap_user = ldap_user
         self._group_type = None
         self._group_search = None
         Loads the settings we need to deal with groups. Raises
         ImproperlyConfigured if anything's not right.
         """
-        self._group_type = ldap_settings.AUTH_LDAP_GROUP_TYPE
+        self._group_type = self.settings.GROUP_TYPE
         if self._group_type is None:
             raise ImproperlyConfigured("AUTH_LDAP_GROUP_TYPE must be an LDAPGroupType instance.")
 
-        self._group_search = ldap_settings.AUTH_LDAP_GROUP_SEARCH
+        self._group_search = self.settings.GROUP_SEARCH
         if self._group_search is None:
             raise ImproperlyConfigured("AUTH_LDAP_GROUP_SEARCH must be an LDAPSearch instance.")
 
         return self._group_infos
 
     def _load_cached_attr(self, attr_name):
-        if ldap_settings.AUTH_LDAP_CACHE_GROUPS:
+        if self.settings.CACHE_GROUPS:
             key = self._cache_key(attr_name)
             value = cache.get(key)
             setattr(self, attr_name, value)
 
     def _cache_attr(self, attr_name):
-        if ldap_settings.AUTH_LDAP_CACHE_GROUPS:
+        if self.settings.CACHE_GROUPS:
             key = self._cache_key(attr_name)
             value = getattr(self, attr_name, None)
-            cache.set(key, value, ldap_settings.AUTH_LDAP_GROUP_CACHE_TIMEOUT)
+            cache.set(key, value, self.settings.GROUP_CACHE_TIMEOUT)
 
     def _cache_key(self, attr_name):
         """
     if they are not specified by the configuration.
     """
     defaults = {
-        'AUTH_LDAP_ALWAYS_UPDATE_USER': True,
-        'AUTH_LDAP_AUTHORIZE_ALL_USERS': False,
-        'AUTH_LDAP_BIND_AS_AUTHENTICATING_USER': False,
-        'AUTH_LDAP_BIND_DN': '',
-        'AUTH_LDAP_BIND_PASSWORD': '',
-        'AUTH_LDAP_CACHE_GROUPS': False,
-        'AUTH_LDAP_CONNECTION_OPTIONS': {},
-        'AUTH_LDAP_DENY_GROUP': None,
-        'AUTH_LDAP_FIND_GROUP_PERMS': False,
-        'AUTH_LDAP_GLOBAL_OPTIONS': {},
-        'AUTH_LDAP_GROUP_CACHE_TIMEOUT': None,
-        'AUTH_LDAP_GROUP_SEARCH': None,
-        'AUTH_LDAP_GROUP_TYPE': None,
-        'AUTH_LDAP_MIRROR_GROUPS': False,
-        'AUTH_LDAP_PROFILE_ATTR_MAP': {},
-        'AUTH_LDAP_PROFILE_FLAGS_BY_GROUP': {},
-        'AUTH_LDAP_REQUIRE_GROUP': None,
-        'AUTH_LDAP_SERVER_URI': 'ldap://localhost',
-        'AUTH_LDAP_START_TLS': False,
-        'AUTH_LDAP_USER_ATTR_MAP': {},
-        'AUTH_LDAP_USER_DN_TEMPLATE': None,
-        'AUTH_LDAP_USER_FLAGS_BY_GROUP': {},
-        'AUTH_LDAP_USER_SEARCH': None,
+        'ALWAYS_UPDATE_USER': True,
+        'AUTHORIZE_ALL_USERS': False,
+        'BIND_AS_AUTHENTICATING_USER': False,
+        'BIND_DN': '',
+        'BIND_PASSWORD': '',
+        'CACHE_GROUPS': False,
+        'CONNECTION_OPTIONS': {},
+        'DENY_GROUP': None,
+        'FIND_GROUP_PERMS': False,
+        'GROUP_CACHE_TIMEOUT': None,
+        'GROUP_SEARCH': None,
+        'GROUP_TYPE': None,
+        'MIRROR_GROUPS': False,
+        'PROFILE_ATTR_MAP': {},
+        'PROFILE_FLAGS_BY_GROUP': {},
+        'REQUIRE_GROUP': None,
+        'SERVER_URI': 'ldap://localhost',
+        'START_TLS': False,
+        'USER_ATTR_MAP': {},
+        'USER_DN_TEMPLATE': None,
+        'USER_FLAGS_BY_GROUP': {},
+        'USER_SEARCH': None,
     }
 
-    def __init__(self):
+    def __init__(self, prefix='AUTH_LDAP_'):
         """
         Loads our settings from django.conf.settings, applying defaults for any
         that are omitted.
         from django.conf import settings
 
         for name, default in self.defaults.iteritems():
-            value = getattr(settings, name, default)
+            value = getattr(settings, prefix + name, default)
             setattr(self, name, value)
-
-
-# Our global settings object
-ldap_settings = LDAPSettings()

File django_auth_ldap/config.py

     ldap = None
     logger = None
 
-    def get_ldap(cls):
+    _ldap_configured = False
+
+    def get_ldap(cls, global_options=None):
         """
         Returns the ldap module. The unit test harness will assign a mock object
         to _LDAPConfig.ldap. It is imperative that the ldap module not be
 
             cls.ldap = ldap
 
+        # Apply global LDAP options once
+        if (not cls._ldap_configured) and (global_options is not None):
+            for opt, value in global_options.iteritems():
+                cls.ldap.set_option(opt, value)
+
+            cls._ldap_configured = True
+
         return cls.ldap
     get_ldap = classmethod(get_ldap)
 

File django_auth_ldap/tests.py

 
     def test_options(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_CONNECTION_OPTIONS={'opt1': 'value1'}
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            CONNECTION_OPTIONS={'opt1': 'value1'}
         )
 
         self.backend.authenticate(username='alice', password='password')
 
     def test_simple_bind(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
 
 
     def test_new_user_lowercase(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
 
 
     def test_new_user_whitespace(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
 
 
     def test_simple_bind_bad_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
 
 
     def test_simple_bind_bad_password(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
 
 
     def test_existing_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         User.objects.create(username='alice')
         user_count = User.objects.count()
 
     def test_existing_user_insensitive(self):
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
             def django_to_ldap_username(self, username):
                 return username[5:]
 
+        self.backend = MyBackend()
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         user_count = User.objects.count()
-        self.backend = MyBackend()
 
         user1 = self.backend.authenticate(username='alice', password='password')
         user2 = self.backend.get_user(user1.pk)
 
     def test_search_bind(self):
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
 
     def test_search_bind_no_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(cn=%(user)s)'
                 )
             )
 
     def test_search_bind_multiple_users(self):
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=*)'
                 )
             )
 
     def test_search_bind_bad_password(self):
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
 
     def test_search_bind_with_credentials(self):
         self._init_settings(
-            AUTH_LDAP_BIND_DN='uid=bob,ou=people,o=test',
-            AUTH_LDAP_BIND_PASSWORD='password',
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            BIND_DN='uid=bob,ou=people,o=test',
+            BIND_PASSWORD='password',
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
 
     def test_search_bind_with_bad_credentials(self):
         self._init_settings(
-            AUTH_LDAP_BIND_DN='uid=bob,ou=people,o=test',
-            AUTH_LDAP_BIND_PASSWORD='bogus',
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            BIND_DN='uid=bob,ou=people,o=test',
+            BIND_PASSWORD='bogus',
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
 
     def test_unicode_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}
         )
 
         user = self.backend.authenticate(username=u'dreßler', password='password')
 
     def test_cidict(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
         )
 
         user = self.backend.authenticate(username="alice", password="password")
 
     def test_populate_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}
         )
 
         user = self.backend.authenticate(username='alice', password='password')
 
     def test_bind_as_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
-            AUTH_LDAP_BIND_AS_AUTHENTICATING_USER=True,
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
+            BIND_AS_AUTHENTICATING_USER=True,
         )
 
         user = self.backend.authenticate(username='alice', password='password')
 
     def test_signal_populate_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
         def handle_populate_user(sender, **kwargs):
             self.assert_('user' in kwargs and 'ldap_user' in kwargs)
         settings.AUTH_PROFILE_MODULE = 'django_auth_ldap.TestProfile'
 
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test'
         )
 
         def handle_user_saved(sender, **kwargs):
 
     def test_no_update_existing(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
-            AUTH_LDAP_ALWAYS_UPDATE_USER=False
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
+            ALWAYS_UPDATE_USER=False
         )
         User.objects.create(username='alice', first_name='Alicia', last_name='Astro')
 
 
     def test_require_group(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_REQUIRE_GROUP="cn=active_gon,ou=groups,o=test"
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            REQUIRE_GROUP="cn=active_gon,ou=groups,o=test"
         )
 
         alice = self.backend.authenticate(username='alice', password='password')
 
     def test_denied_group(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_DENY_GROUP="cn=active_gon,ou=groups,o=test"
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            DENY_GROUP="cn=active_gon,ou=groups,o=test"
         )
 
         alice = self.backend.authenticate(username='alice', password='password')
 
     def test_group_dns(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
         )
         self.mock_ldap.set_return_value('search_s',
             ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
 
     def test_group_names(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
         )
         self.mock_ldap.set_return_value('search_s',
             ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
 
     def test_dn_group_membership(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_USER_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_gon,ou=groups,o=test",
                 'is_staff': "cn=staff_gon,ou=groups,o=test",
                 'is_superuser': "cn=superuser_gon,ou=groups,o=test"
 
     def test_posix_membership(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=PosixGroupType(),
-            AUTH_LDAP_USER_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=PosixGroupType(),
+            USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_px,ou=groups,o=test",
                 'is_staff': "cn=staff_px,ou=groups,o=test",
                 'is_superuser': "cn=superuser_px,ou=groups,o=test"
 
     def test_nested_dn_group_membership(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_USER_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'),
+            USER_FLAGS_BY_GROUP={
                 'is_active': "cn=parent_gon,ou=groups,o=test",
                 'is_staff': "cn=parent_gon,ou=groups,o=test",
             }
 
     def test_posix_missing_attributes(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=PosixGroupType(),
-            AUTH_LDAP_USER_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=PosixGroupType(),
+            USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_px,ou=groups,o=test"
             }
         )
         settings.AUTH_PROFILE_MODULE = 'django_auth_ldap.TestProfile'
 
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_PROFILE_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            PROFILE_FLAGS_BY_GROUP={
                 'is_special': "cn=superuser_gon,ou=groups,o=test"
             }
         )
 
     def test_dn_group_permissions(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_FIND_GROUP_PERMS=True
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            FIND_GROUP_PERMS=True
         )
         self._init_groups()
         self.mock_ldap.set_return_value('search_s',
 
     def test_empty_group_permissions(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_FIND_GROUP_PERMS=True
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            FIND_GROUP_PERMS=True
         )
         self._init_groups()
         self.mock_ldap.set_return_value('search_s',
 
     def test_posix_group_permissions(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
                 self.mock_ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
             ),
-            AUTH_LDAP_GROUP_TYPE=PosixGroupType(),
-            AUTH_LDAP_FIND_GROUP_PERMS=True
+            GROUP_TYPE=PosixGroupType(),
+            FIND_GROUP_PERMS=True
         )
         self._init_groups()
         self.mock_ldap.set_return_value('search_s',
 
     def test_foreign_user_permissions(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_FIND_GROUP_PERMS=True
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            FIND_GROUP_PERMS=True
         )
         self._init_groups()
 
 
     def test_group_cache(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_FIND_GROUP_PERMS=True,
-            AUTH_LDAP_CACHE_GROUPS=True
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            FIND_GROUP_PERMS=True,
+            CACHE_GROUPS=True
         )
         self._init_groups()
         self.mock_ldap.set_return_value('search_s',
 
     def test_group_mirroring(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
                 self.mock_ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
             ),
-            AUTH_LDAP_GROUP_TYPE=PosixGroupType(),
-            AUTH_LDAP_MIRROR_GROUPS=True,
+            GROUP_TYPE=PosixGroupType(),
+            MIRROR_GROUPS=True,
         )
         self.mock_ldap.set_return_value('search_s',
             ("ou=groups,o=test", 2, "(&(objectClass=posixGroup)(|(gidNumber=1000)(memberUid=alice)))", None, 0),
 
     def test_nested_group_mirroring(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_MIRROR_GROUPS=True,
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'),
+            MIRROR_GROUPS=True,
         )
         self.mock_ldap.set_return_value('search_s',
             ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=uid=alice,ou=people,o=test)))", None, 0),
 
     def test_authorize_external_users(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=MemberDNGroupType(member_attr='member'),
-            AUTH_LDAP_FIND_GROUP_PERMS=True,
-            AUTH_LDAP_AUTHORIZE_ALL_USERS=True
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=MemberDNGroupType(member_attr='member'),
+            FIND_GROUP_PERMS=True,
+            AUTHORIZE_ALL_USERS=True
         )
         self._init_groups()
         self.mock_ldap.set_return_value('search_s',
 
     def test_create_without_auth(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
         )
 
         alice = self.backend.populate_user('alice')
 
     def test_populate_without_auth(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_ALWAYS_UPDATE_USER=False,
-            AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
-            AUTH_LDAP_GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
-            AUTH_LDAP_GROUP_TYPE=GroupOfNamesType(),
-            AUTH_LDAP_USER_FLAGS_BY_GROUP={
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            ALWAYS_UPDATE_USER=False,
+            USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'},
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_TYPE=GroupOfNamesType(),
+            USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_gon,ou=groups,o=test",
                 'is_staff': "cn=staff_gon,ou=groups,o=test",
                 'is_superuser': "cn=superuser_gon,ou=groups,o=test"
 
     def test_populate_bogus_user(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
         )
 
         bogus = self.backend.populate_user('bogus')
 
     def test_start_tls_missing(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_START_TLS=False,
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            START_TLS=False,
         )
 
         self.assert_(not self.mock_ldap.tls_enabled)
 
     def test_start_tls(self):
         self._init_settings(
-            AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            AUTH_LDAP_START_TLS=True,
+            USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
+            START_TLS=True,
         )
 
         self.assert_(not self.mock_ldap.tls_enabled)
         are, but we filter those out so we don't trip over them.
         """
         self._init_settings(
-            AUTH_LDAP_USER_SEARCH=LDAPSearch(
+            USER_SEARCH=LDAPSearch(
                 "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
                 )
             )
 
 
     def _init_settings(self, **kwargs):
-        backend.ldap_settings = TestSettings(**kwargs)
+        self.backend.settings = TestSettings(**kwargs)
 
     def _init_groups(self):
         permissions = [

File docs/index.rst

 feature of authentication, not authorization.
 
 
+Multiple LDAP Configs
+=====================
+
+You've probably noticed that all of the settings for this backend have the
+prefix AUTH_LDAP\_. This is the default, but it can be customized by subclasses
+of :class:`~django_auth_ldap.backend.LDAPBackend`. The main reason you would
+want to do this is to create two backend subclasses that reference different
+collections of settings and thus operate independently. For example, you might
+have two separate LDAP servers that you want to authenticate against. A short
+example should demonstrate this:
+
+.. code-block:: python
+
+    # mypackage.ldap
+
+    from django_auth_ldap.backend import LDAPBackend
+
+    class LDAPBackend1(LDAPBackend):
+        settings_prefix = "AUTH_LDAP_1_"
+
+    class LDAPBackend2(LDAPBackend):
+        settings_prefix = "AUTH_LDAP_2_" 
+
+
+.. code-block:: python
+
+    # settings.py
+
+    AUTH_LDAP_1_SERVER_URI = "ldap://ldap1.example.com"
+    AUTH_LDAP_1_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com"
+
+    AUTH_LDAP_2_SERVER_URI = "ldap://ldap2.example.com"
+    AUTH_LDAP_2_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com"
+
+    AUTHENTICATION_BACKENDS = (
+        "mypackage.ldap.LDAPBackend1",
+        "mypackage.ldap.LDAPBackend2",
+    )
+
+All of the usual rules apply: Django will attempt to authenticate a user with
+each backend in turn until one of them succeeds. When a particular backend
+successfully authenticates a user, that user will be linked to the backend for
+the duration of their session.
+
+
 Logging
 =======
 
     :class:`~django_auth_ldap.backend.LDAPBackend` has one method that may be
     called directly and several that may be overridden in subclasses.
 
+    .. data:: settings_prefix
+
+        A prefix for all of our Django settings. By default, this is
+        ``"AUTH_LDAP_"``, but subclasses can override this. When different
+        subclasses use different prefixes, they can both be installed and
+        operate independently.
+
     .. method:: populate_user(username)
 
         Populates the Django user for the given LDAP username. This connects to