Commits

Theo Chatzimichos committed 54b75f4

Replace MockLDAP with the external mockldap package

Comments (0)

Files changed (4)

django_auth_ldap/backend.py

 Additional classes can be found in the config module next to this one.
 """
 
+import ldap
 import sys
 import traceback
 import pprint
             user = self._user
         except self.AuthenticationFailed, e:
             logger.debug(u"Authentication failed for %s" % self._username)
-        except self.ldap.LDAPError, e:
+        except ldap.LDAPError, e:
             logger.warning(u"Caught LDAPError while authenticating %s: %s",
                            self._username, pprint.pformat(e))
         except Exception:
             if self.settings.FIND_GROUP_PERMS:
                 try:
                     self._load_group_permissions()
-                except self.ldap.LDAPError, e:
+                except ldap.LDAPError as e:
                     logger.warning("Caught LDAPError loading group permissions: %s",
                                    pprint.pformat(e))
 
                 self._get_or_create_user(force_populate=True)
 
             user = self._user
-        except self.ldap.LDAPError, e:
+        except ldap.LDAPError, e:
             logger.warning(u"Caught LDAPError while authenticating %s: %s",
                            self._username, pprint.pformat(e))
         except Exception, e:
             sticky = self.settings.BIND_AS_AUTHENTICATING_USER
 
             self._bind_as(self.dn, password, sticky=sticky)
-        except self.ldap.INVALID_CREDENTIALS:
+        except ldap.INVALID_CREDENTIALS:
             raise self.AuthenticationFailed("User DN/password rejected by LDAP server.")
 
     def _load_user_attrs(self):
         if self.dn is not None:
-            search = LDAPSearch(self.dn, self.ldap.SCOPE_BASE)
+            search = LDAPSearch(self.dn, ldap.SCOPE_BASE)
             results = search.execute(self.connection)
 
             if results is not None and len(results) > 0:
 
     def _construct_simple_user_dn(self):
         template = self.settings.USER_DN_TEMPLATE
-        username = self.ldap.dn.escape_dn_chars(self._username)
+        username = ldap.dn.escape_dn_chars(self._username)
 
         self._user_dn = template % {'user': username}
 
         Returns our cached LDAPObject, which may or may not be bound.
         """
         if self._connection is None:
-            self._connection = self.ldap.initialize(self.settings.SERVER_URI)
+            self._connection = ldap.initialize(self.settings.SERVER_URI)
 
             for opt, value in self.settings.CONNECTION_OPTIONS.iteritems():
                 self._connection.set_option(opt, value)

django_auth_ldap/config.py

 notes on naming conventions.
 """
 
+import ldap
 import logging
 import pprint
 
         of python-ldap.
         """
         if cls.ldap is None:
-            import ldap
             import ldap.filter
 
             # Support for python-ldap < 2.0.6
             results = connection.search_s(self.base_dn.encode('utf-8'),
                                           self.scope,
                                           filterstr.encode('utf-8'))
-        except self.ldap.LDAPError, e:
+        except ldap.LDAPError, e:
             results = []
             logger.error(u"search_s('%s', %d, '%s') raised %s" %
                          (self.base_dn, self.scope, filterstr, pprint.pformat(e)))
             filterstr = self.filterstr % filterargs
             msgid = connection.search(self.base_dn.encode('utf-8'),
                                       self.scope, filterstr.encode('utf-8'))
-        except self.ldap.LDAPError, e:
+        except ldap.LDAPError, e:
             msgid = None
             logger.error(u"search('%s', %d, '%s') raised %s" %
                          (self.base_dn, self.scope, filterstr, pprint.pformat(e)))
         """
         try:
             kind, results = connection.result(msgid)
-            if kind != self.ldap.RES_SEARCH_RESULT:
+            if kind != ldap.RES_SEARCH_RESULT:
                 results = []
-        except self.ldap.LDAPError, e:
+        except ldap.LDAPError, e:
             results = []
             logger.error(u"result(%d) raised %s" % (msgid, pprint.pformat(e)))
 
 
             try:
                 is_member = ldap_user.connection.compare_s(group_dn.encode('utf-8'), 'memberUid', user_uid.encode('utf-8'))
-            except self.ldap.NO_SUCH_ATTRIBUTE:
+            except ldap.UNDEFINED_TYPE:
                 is_member = False
 
             if not is_member:
                 try:
                     is_member = ldap_user.connection.compare_s(group_dn.encode('utf-8'), 'gidNumber', user_gid.encode('utf-8'))
-                except self.ldap.NO_SUCH_ATTRIBUTE:
+                except ldap.UNDEFINED_TYPE:
                     is_member = False
         except (KeyError, IndexError):
             is_member = False
                 self.member_attr.encode('utf-8'),
                 ldap_user.dn.encode('utf-8')
             )
-        except self.ldap.NO_SUCH_ATTRIBUTE:
+        except ldap.UNDEFINED_TYPE:
             result = 0
 
         return result

django_auth_ldap/tests.py

 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from collections import defaultdict
+
 from copy import deepcopy
+from mockldap import MockLdap
+import ldap
 import logging
 import pickle
-import sys
-import re
 
 from django.conf import settings
 import django.db.models.signals
 
 from django_auth_ldap.models import TestUser, TestProfile
 from django_auth_ldap import backend
-from django_auth_ldap.config import _LDAPConfig, LDAPSearch, LDAPSearchUnion
+from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
 from django_auth_ldap.config import PosixGroupType, MemberDNGroupType, NestedMemberDNGroupType
 from django_auth_ldap.config import GroupOfNamesType
 
             setattr(self, name, value)
 
 
-class MockLDAP(object):
-    """
-    This is a stand-in for the python-ldap module; it serves as both the ldap
-    module and the LDAPObject class. While it's temping to add some real LDAP
-    capabilities here, this is designed to remain as simple as possible, so as
-    to minimize the risk of creating bogus unit tests through a buggy test
-    harness.
+class LDAPTest(TestCase):
+    top = ("o=test", {"o": "test"})
+    people = ("ou=people,o=test", {"ou": "people"})
+    groups = ("ou=groups,o=test", {"ou": "groups"})
 
-    Simple operations can be simulated, but for nontrivial searches, the client
-    will have to seed the mock object with return values for expected API calls.
-    This may sound like cheating, but it's really no more so than a simulated
-    LDAP server. The fact is we can not require python-ldap to be installed in
-    order to run the unit tests, so all we can do is verify that LDAPBackend is
-    calling the APIs that we expect.
-
-    set_return_value takes the name of an API, a tuple of arguments, and a
-    return value. Every time an API is called, it looks for a predetermined
-    return value based on the arguments received. If it finds one, then it
-    returns it, or raises it if it's an Exception. If it doesn't find one, then
-    it tries to satisfy the request internally. If it can't, it raises a
-    PresetReturnRequiredError.
-
-    At any time, the client may call ldap_methods_called_with_arguments() or
-    ldap_methods_called() to get a record of all of the LDAP API calls that have
-    been made, with or without arguments.
-    """
-    class PresetReturnRequiredError(Exception):
-        pass
-
-    SCOPE_BASE = 0
-    SCOPE_ONELEVEL = 1
-    SCOPE_SUBTREE = 2
-
-    RES_SEARCH_RESULT = 101
-
-    class LDAPError(Exception):
-        pass
-
-    class INVALID_CREDENTIALS(LDAPError):
-        pass
-
-    class NO_SUCH_OBJECT(LDAPError):
-        pass
-
-    class NO_SUCH_ATTRIBUTE(LDAPError):
-        pass
-
-    #
-    # Submodules
-    #
-    class dn(object):
-        def escape_dn_chars(s):
-            return s
-        escape_dn_chars = staticmethod(escape_dn_chars)
-
-    class filter(object):
-        def escape_filter_chars(s):
-            return s
-        escape_filter_chars = staticmethod(escape_filter_chars)
-
-    class cidict(object):
-        class cidict(dict):
-            pass
-
-    def __init__(self, directory):
-        """
-        directory is a complex structure with the entire contents of the
-        mock LDAP directory. directory must be a dictionary mapping
-        distinguished names to dictionaries of attributes. Each attribute
-        dictionary maps attribute names to lists of values. e.g.:
-
-        {
-            "uid=alice,ou=users,dc=example,dc=com":
-            {
-                "uid": ["alice"],
-                "userPassword": ["secret"],
-            },
-        }
-        """
-        self.directory = self.cidict.cidict(directory)
-
-        self.reset()
-
-    def reset(self):
-        """
-        Resets our recorded API calls and queued return values as well as
-        miscellaneous configuration options.
-        """
-        self.calls = []
-        self.return_value_maps = defaultdict(lambda: {})
-        self.async_results = []
-        self.options = {}
-        self.tls_enabled = False
-
-    def set_return_value(self, api_name, arguments, value):
-        """
-        Stores a preset return value for a given API with a given set of
-        arguments.
-        """
-        self.return_value_maps[api_name][arguments] = value
-
-    def ldap_methods_called_with_arguments(self):
-        """
-        Returns a list of 2-tuples, one for each API call made since the last
-        reset. Each tuple contains the name of the API and a dictionary of
-        arguments. Argument defaults are included.
-        """
-        return self.calls
-
-    def ldap_methods_called(self):
-        """
-        Returns the list of API names called.
-        """
-        return [call[0] for call in self.calls]
-
-    #
-    # Begin LDAP methods
-    #
-
-    def set_option(self, option, invalue):
-        self._record_call('set_option', {
-            'option': option,
-            'invalue': invalue
-        })
-
-        self.options[option] = invalue
-
-    def initialize(self, uri, trace_level=0, trace_file=sys.stdout, trace_stack_limit=None):
-        self._record_call('initialize', {
-            'uri': uri,
-            'trace_level': trace_level,
-            'trace_file': trace_file,
-            'trace_stack_limit': trace_stack_limit
-        })
-
-        value = self._get_return_value('initialize',
-            (uri, trace_level, trace_file, trace_stack_limit))
-        if value is None:
-            value = self
-
-        return value
-
-    def simple_bind_s(self, who='', cred=''):
-        self._record_call('simple_bind_s', {
-            'who': who,
-            'cred': cred
-        })
-
-        value = self._get_return_value('simple_bind_s', (who, cred))
-        if value is None:
-            value = self._simple_bind_s(who, cred)
-
-        return value
-
-    def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
-        self._record_call('search', {
-            'base': base,
-            'scope': scope,
-            'filterstr': filterstr,
-            'attrlist': attrlist,
-            'attrsonly': attrsonly
-        })
-
-        value = self._get_return_value('search_s',
-            (base, scope, filterstr, attrlist, attrsonly))
-        if value is None:
-            value = self._search_s(base, scope, filterstr, attrlist, attrsonly)
-
-        return self._add_async_result(value)
-
-    def result(self, msgid, all=1, timeout=None):
-        self._record_call('result', {
-            'msgid': msgid,
-            'all': all,
-            'timeout': timeout,
-        })
-
-        return self.RES_SEARCH_RESULT, self._pop_async_result(msgid)
-
-    def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
-        self._record_call('search_s', {
-            'base': base,
-            'scope': scope,
-            'filterstr': filterstr,
-            'attrlist': attrlist,
-            'attrsonly': attrsonly
-        })
-
-        value = self._get_return_value('search_s',
-            (base, scope, filterstr, attrlist, attrsonly))
-        if value is None:
-            value = self._search_s(base, scope, filterstr, attrlist, attrsonly)
-
-        return value
-
-    def start_tls_s(self):
-        self.tls_enabled = True
-
-    def compare_s(self, dn, attr, value):
-        self._record_call('compare_s', {
-            'dn': dn,
-            'attr': attr,
-            'value': value
-        })
-
-        result = self._get_return_value('compare_s', (dn, attr, value))
-        if result is None:
-            result = self._compare_s(dn, attr, value)
-
-        # print "compare_s('%s', '%s', '%s'): %d" % (dn, attr, value, result)
-
-        return result
-
-    def unbind(self):
-        pass
-
-    #
-    # Internal implementations
-    #
-
-    def _simple_bind_s(self, who='', cred=''):
-        success = False
-
-        if(who == '' and cred == ''):
-            success = True
-        elif self._compare_s(who.lower(), 'userPassword', cred):
-            success = True
-
-        if success:
-            return (97, []) # python-ldap returns this; I don't know what it means
-        else:
-            raise self.INVALID_CREDENTIALS('%s:%s' % (who, cred))
-
-    def _compare_s(self, dn, attr, value):
-        if dn not in self.directory:
-            raise self.NO_SUCH_OBJECT
-
-        if attr not in self.directory[dn]:
-            raise self.NO_SUCH_ATTRIBUTE
-
-        return (value in self.directory[dn][attr]) and 1 or 0
-
-    def _search_s(self, base, scope, filterstr, attrlist, attrsonly):
-        """
-        We can do a search with a filter on the form (attr=value), where value
-        can be a string or *. Beyond that, you're on your own.
-        """
-
-        valid_filterstr = re.compile(r'\(\w+=([\w@.]+|[*])\)')
-
-        if not valid_filterstr.match(filterstr):
-            raise self.PresetReturnRequiredError('search_s("%s", %d, "%s", "%s", %d)' %
-                (base, scope, filterstr, attrlist, attrsonly))
-
-        def get_results(dn, filterstr, results):
-            attrs = self.directory.get(dn)
-            attr, value = filterstr[1:-1].split('=')
-            if attrs and attr in attrs.keys() and str(value) in attrs[attr] or value == u'*':
-                results.append((dn, attrs))
-
-        results = []
-        all_dn = self.directory.keys()
-        if scope == self.SCOPE_BASE:
-            get_results(base, filterstr, results)
-        elif scope == self.SCOPE_ONELEVEL:
-            for dn in all_dn:
-                if len(dn.split('=')) == len(base.split('=')) + 1 and dn.endswith(base):
-                    get_results(dn, filterstr, results)
-        elif scope == self.SCOPE_SUBTREE:
-            for dn in all_dn:
-                if dn.endswith(base):
-                    get_results(dn, filterstr, results)
-        if results:
-            return results
-        else:
-            raise self.NO_SUCH_OBJECT()
-
-    def _add_async_result(self, value):
-        self.async_results.append(value)
-
-        return len(self.async_results) - 1
-
-    def _pop_async_result(self, msgid):
-        if msgid in xrange(len(self.async_results)):
-            value = self.async_results[msgid]
-            self.async_results[msgid] = None
-        else:
-            value = None
-
-        return value
-
-    #
-    # Utils
-    #
-
-    def _record_call(self, api_name, arguments):
-        self.calls.append((api_name, arguments))
-
-    def _get_return_value(self, api_name, arguments):
-        try:
-            value = self.return_value_maps[api_name][arguments]
-        except KeyError:
-            value = None
-
-        if isinstance(value, Exception):
-            raise value
-
-        return value
-
-
-class LDAPTest(TestCase):
-    # Following are the objecgs in our mock LDAP directory
     alice = ("uid=alice,ou=people,o=test", {
         "uid": ["alice"],
         "objectClass": ["person", "organizationalPerson", "inetOrgPerson", "posixAccount"],
         "member": ["cn=parent_gon,ou=groups,o=test"]
     })
 
-    mock_ldap = MockLDAP({
-        alice[0]: alice[1],
-        bob[0]: bob[1],
-        dressler[0]: dressler[1],
-        nobody[0]: nobody[1],
-        active_px[0]: active_px[1],
-        staff_px[0]: staff_px[1],
-        superuser_px[0]: superuser_px[1],
-        active_gon[0]: active_gon[1],
-        staff_gon[0]: staff_gon[1],
-        superuser_gon[0]: superuser_gon[1],
-        parent_gon[0]: parent_gon[1],
-        nested_gon[0]: nested_gon[1],
-        circular_gon[0]: circular_gon[1],
-    })
+    directory = dict([top, people, groups, alice, bob, dressler, nobody,
+                      active_px, staff_px, superuser_px, active_gon, staff_gon,
+                      superuser_gon, parent_gon, nested_gon, circular_gon])
 
     logging_configured = False
 
             cls.logging_configured = True
     configure_logger = classmethod(configure_logger)
 
+    @classmethod
+    def setUpClass(cls):
+        cls.mockldap = MockLdap(cls.directory)
+
     def setUp(self):
         self.configure_logger()
 
-        self.ldap = _LDAPConfig.ldap = self.mock_ldap
+        self.mockldap.start()
+        self.ldapobj = self.mockldap['ldap://localhost']
 
         self.backend = backend.LDAPBackend()
         self.backend.ldap # Force global configuration
 
-        self.mock_ldap.reset()
-
     def tearDown(self):
-        pass
+        self.mockldap.stop()
+        del self.ldapobj
 
     def test_options(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
             CONNECTION_OPTIONS={'opt1': 'value1'}
         )
-
         self.backend.authenticate(username='alice', password='password')
 
-        self.assertEqual(self.mock_ldap.options, {'opt1': 'value1'})
+        self.assertEqual(self.ldapobj.get_option('opt1'), 'value1')
 
     def test_simple_bind(self):
         self._init_settings(
         self.assert_(not user.has_usable_password())
         self.assertEqual(user.username, 'alice')
         self.assertEqual(User.objects.count(), user_count + 1)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_new_user_lowercase(self):
         self.assert_(not user.has_usable_password())
         self.assertEqual(user.username, 'alice')
         self.assertEqual(User.objects.count(), user_count + 1)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_deepcopy(self):
         )
 
         user = self.backend.authenticate(username='Alice', password='password')
-
         self.assert_(isinstance(user, TestUser))
 
     @override_settings(AUTH_USER_MODEL='django_auth_ldap.TestUser')
 
         self.assert_(user is None)
         self.assertEqual(User.objects.count(), user_count)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_simple_bind_bad_password(self):
 
         self.assert_(user is None)
         self.assertEqual(User.objects.count(), user_count)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_existing_user(self):
     def test_existing_user_insensitive(self):
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=Alice)", None, 0), [self.alice])
+        self.ldapobj.search_s.seed("ou=people,o=test", 2, "(uid=Alice)")([self.alice])
         User.objects.create(username='alice')
 
         user = self.backend.authenticate(username='Alice', password='password')
     def test_search_bind(self):
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=alice)", None, 0), [self.alice])
         user_count = User.objects.count()
 
         user = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(user is not None)
         self.assertEqual(User.objects.count(), user_count + 1)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'])
 
     def test_search_bind_no_user(self):
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(cn=%(user)s)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(cn=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(cn=alice)", None, 0), [])
 
         user = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(user is None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s'])
 
     def test_search_bind_multiple_users(self):
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=*)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=*)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=*)", None, 0), [self.alice, self.bob])
 
         user = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(user is None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s'])
 
     def test_search_bind_bad_password(self):
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=alice)", None, 0), [self.alice])
 
         user = self.backend.authenticate(username='alice', password='bogus')
 
         self.assert_(user is None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'])
 
     def test_search_bind_with_credentials(self):
             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)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=alice)", None, 0), [self.alice])
 
         user = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(user is not None)
         self.assert_(user.ldap_user is not None)
         self.assertEqual(user.ldap_user.dn, self.alice[0])
-        self.assertEqual(user.ldap_user.attrs, self.alice[1])
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(user.ldap_user.attrs, ldap.cidict.cidict(self.alice[1]))
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'])
 
     def test_search_bind_with_bad_credentials(self):
             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)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
 
         user = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(user is None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_unicode_user(self):
         )
 
         user = self.backend.authenticate(username=u'dreßler', password='password')
-
         self.assert_(user is not None)
         self.assertEqual(user.username, u'dreßler')
         self.assertEqual(user.last_name, u'Dreßler')
         )
 
         user = self.backend.authenticate(username="alice", password="password")
-
-        self.assert_(isinstance(user.ldap_user.attrs, self.ldap.cidict.cidict))
+        self.assert_(isinstance(user.ldap_user.attrs, ldap.cidict.cidict))
 
     def test_populate_user(self):
         self._init_settings(
         self.assertEqual(user.last_name, 'Adams')
 
         # init, bind as user, bind anonymous, lookup user attrs
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'simple_bind_s', 'search_s'])
 
     def test_bind_as_user(self):
         self.assertEqual(user.last_name, 'Adams')
 
         # init, bind as user, lookup user attrs
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s'])
 
     def test_signal_populate_user(self):
     def test_require_group(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             REQUIRE_GROUP="cn=active_gon,ou=groups,o=test"
         )
 
         self.assert_(alice is not None)
         self.assert_(bob is None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s', 'initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s'])
 
     def test_denied_group(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             DENY_GROUP="cn=active_gon,ou=groups,o=test"
         )
 
         self.assert_(alice is None)
         self.assert_(bob is not None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s', 'initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s'])
 
     def test_group_dns(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
-
         alice = self.backend.authenticate(username='alice', password='password')
 
         self.assertEqual(alice.ldap_user.group_dns, set((g[0].lower() for g in [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon])))
     def test_group_names(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
-
         alice = self.backend.authenticate(username='alice', password='password')
 
         self.assertEqual(alice.ldap_user.group_names, set(['active_gon', 'staff_gon', 'superuser_gon', 'nested_gon']))
     def test_dn_group_membership(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_gon,ou=groups,o=test",
     def test_posix_membership(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=PosixGroupType(),
             USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_px,ou=groups,o=test",
     def test_nested_dn_group_membership(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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",
             }
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=uid=alice,ou=people,o=test)))", None, 0),
-            [self.active_gon, self.nested_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=active_gon,ou=groups,o=test)(member=cn=nested_gon,ou=groups,o=test)))", None, 0),
-            [self.parent_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=parent_gon,ou=groups,o=test)))", None, 0),
-            [self.circular_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=circular_gon,ou=groups,o=test)))", None, 0),
-            [self.nested_gon]
-        )
-
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=uid=bob,ou=people,o=test)))", None, 0),
-            []
-        )
-
         alice = self.backend.authenticate(username='alice', password='password')
         bob = self.backend.authenticate(username='bob', password='password')
 
     def test_posix_missing_attributes(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=PosixGroupType(),
             USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_px,ou=groups,o=test"
 
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             FIND_GROUP_PERMS=True
         )
         self._init_groups()
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
 
         alice = User.objects.create(username='alice')
         alice = self.backend.get_user(alice.pk)
     def test_empty_group_permissions(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             FIND_GROUP_PERMS=True
         )
         self._init_groups()
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=bob,ou=people,o=test))", None, 0),
-            []
-        )
 
         bob = User.objects.create(username='bob')
         bob = self.backend.get_user(bob.pk)
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
             GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
-                self.mock_ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
+                ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
             ),
             GROUP_TYPE=PosixGroupType(),
             FIND_GROUP_PERMS=True
         )
         self._init_groups()
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=posixGroup)(|(gidNumber=1000)(memberUid=alice)))", None, 0),
-            [self.active_px, self.staff_px, self.superuser_px]
-        )
 
         alice = User.objects.create(username='alice')
         alice = self.backend.get_user(alice.pk)
     def test_foreign_user_permissions(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             FIND_GROUP_PERMS=True
         )
     def test_group_cache(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=bob,ou=people,o=test))", None, 0),
-            []
-        )
 
         alice_id = User.objects.create(username='alice').pk
         bob_id = User.objects.create(username='bob').pk
             self.assertEqual(self.backend.get_group_permissions(bob), set())
 
         # Should have executed one LDAP search per user
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search_s', 'initialize', 'simple_bind_s', 'search_s'])
 
     def test_group_mirroring(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
             GROUP_SEARCH=LDAPSearch('ou=groups,o=test',
-                self.mock_ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
+                ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)"
             ),
             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),
-            [self.active_px, self.staff_px, self.superuser_px]
-        )
 
         self.assertEqual(Group.objects.count(), 0)
 
     def test_nested_group_mirroring(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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),
-            [self.active_gon, self.nested_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=active_gon,ou=groups,o=test)(member=cn=nested_gon,ou=groups,o=test)))", None, 0),
-            [self.parent_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=parent_gon,ou=groups,o=test)))", None, 0),
-            [self.circular_gon]
-        )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(|(member=cn=circular_gon,ou=groups,o=test)))", None, 0),
-            [self.nested_gon]
-        )
+        self.ldapobj.search_s.seed(
+            "ou=groups,o=test", 2, "(&(objectClass=*)(|(member=uid=alice,ou=people,o=test)))")([self.active_gon, self.nested_gon])
 
         alice = self.backend.authenticate(username='alice', password='password')
 
     def test_authorize_external_users(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', 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',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
 
         alice = User.objects.create(username='alice')
 
             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_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=GroupOfNamesType(),
             USER_FLAGS_BY_GROUP={
                 'is_active': "cn=active_gon,ou=groups,o=test",
             START_TLS=False,
         )
 
-        self.assert_(not self.mock_ldap.tls_enabled)
+        self.assert_(not self.ldapobj.tls_enabled)
         self.backend.authenticate(username='alice', password='password')
-        self.assert_(not self.mock_ldap.tls_enabled)
+        self.assert_(not self.ldapobj.tls_enabled)
 
     def test_start_tls(self):
         self._init_settings(
             START_TLS=True,
         )
 
-        self.assert_(not self.mock_ldap.tls_enabled)
+        self.assert_(not self.ldapobj.tls_enabled)
         self.backend.authenticate(username='alice', password='password')
-        self.assert_(self.mock_ldap.tls_enabled)
+        self.assert_(self.ldapobj.tls_enabled)
 
     def test_null_search_results(self):
         """
         """
         self._init_settings(
             USER_SEARCH=LDAPSearch(
-                "ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
+                "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=alice)", None, 0), [self.alice, (None, '')])
-
         self.backend.authenticate(username='alice', password='password')
 
     def test_union_search(self):
         self._init_settings(
             USER_SEARCH=LDAPSearchUnion(
-                LDAPSearch("ou=groups,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'),
-                LDAPSearch("ou=people,o=test", self.mock_ldap.SCOPE_SUBTREE, '(uid=%(user)s)'),
+                LDAPSearch("ou=groups,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'),
+                LDAPSearch("ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'),
             )
         )
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(uid=alice)", None, 0), [])
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=people,o=test", 2, "(uid=alice)", None, 0), [self.alice])
-
         alice = self.backend.authenticate(username='alice', password='password')
 
         self.assert_(alice is not None)
 
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s', 'search', 'search', 'result',
                 'result', 'simple_bind_s'])
 
         alice = self.backend.authenticate(username=u'alice', password=u'')
 
         self.assertEqual(alice, None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(), [])
+        self.assertEqual(self.ldapobj.methods_called(), [])
 
     def test_permit_empty_password(self):
         self._init_settings(
         alice = self.backend.authenticate(username=u'alice', password=u'')
 
         self.assertEqual(alice, None)
-        self.assertEqual(self.mock_ldap.ldap_methods_called(),
+        self.assertEqual(self.ldapobj.methods_called(),
             ['initialize', 'simple_bind_s'])
 
     def test_pickle(self):
         self._init_settings(
             USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test',
-            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', self.mock_ldap.SCOPE_SUBTREE),
+            GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE),
             GROUP_TYPE=MemberDNGroupType(member_attr='member'),
             FIND_GROUP_PERMS=True
         )
         self._init_groups()
-        self.mock_ldap.set_return_value('search_s',
-            ("ou=groups,o=test", 2, "(&(objectClass=*)(member=uid=alice,ou=people,o=test))", None, 0),
-            [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]
-        )
 
         alice0 = self.backend.authenticate(username=u'alice', password=u'password')
 
     ],
     setup_requires=[
         "setuptools >= 0.6c11",
+    ],
+    tests_require=[
+        "mockldap >= 0.1",
     ]
 )