how to create mock ldap server for django project?

  • Last Update :
  • Techknowledgy :

Unit tests include a mock ldap test harness. The python-ldap module will not be imported during unit tests. Tests pass in Python 2.3, 2.4, 2.5, and 2.6 both in the context of a project and from tests/runtests.py. , In addition to the unit tests, I have tested this against slapd on GNU/Linux; I haven't gotten access to other LDAP servers for testing yet. It would be good to do a pass against ActiveDirectory at least. , While testing on Centos 5.3 (which runs python 2.4 with python-ldap 2.2.0) I had problems with backend.py:304: ,Implementation is located in django/contrib/auth/contrib/ldap/. Of necessity, it is split into two modules: one with the backend itself and the other with classes used for configuration and support. The second is safe to import into settings.py.

username = self.ldap.dn.escape_dn_chars(self._username)
from ldap
import dn
username = dn.escape_dn_chars(self._username)
  • Now I put this in my global settings.py
AUTHENTICATION_BACKENDS = (
   'django.contrib.auth.contrib.ldap.backend.LDAPBackend',
   'django.contrib.auth.backends.ModelBackend',
)
AUTH_LDAP_SERVER_URI = "ldap://discovery.smsc.com"
AUTH_LDAP_USER_DN_TEMPLATE = "cn=%(user)s,o=SMSC,objectclass=person"

Now when I login I get an exception:

   Error importing authentication backend django.contrib.auth.contrib.ldap.backend: "No module named contrib.ldap.backend"

Suggestion : 2

The goal of this module is to provide a simple way to mock ldap backend servers for your unittests. It makes it possible to define upfront a set of directory entries that can be queried or set fixed return values to ldap queries. It acts as a drop in replacement for the LDAPObject class of the python-ldap module. It implements a subset of the allowed methods of this class.,The MockLDAP class replaces the LDAPObject of the python-ldap module. The easiest way to use it, is to overwrite ldap.initialize to return MockLDAP instead of LDAPObject. The example below uses Michael Foord's Mock library to achieve that:,This module implements the MockLDAP class that functions both as the LDAPObject as well as the ldap module. Most of the code and design has been taken from Peter Sagerson's excellent django-auth-ldap module.,Besides of fixing return values for specific calls, you can also imitate a full ldap server with a directory of entries:

Get and install the code:

$ git clone git: //github.com/zulip/fakeldap.git
   $ cd fakeldap
$ python setup.py install

If you want, you can run the tests:

$ python setup.py nosetests

The MockLDAP class replaces the LDAPObject of the python-ldap module. The easiest way to use it, is to overwrite ldap.initialize to return MockLDAP instead of LDAPObject. The example below uses Michael Foord's Mock library to achieve that:

import unittest
from mock
import patch
from fakeldap
import MockLDAP

_mock_ldap = MockLDAP()

class YourTestCase(unittest.TestCase):
   def setUp(self):
   # Patch where the ldap library is used:
   self.ldap_patcher = patch('app.module.ldap.initialize')
self.mock_ldap = self.ldap_patcher.start()
self.mock_ldap.return_value = _mock_ldap

def tearDown(self):
   _mock_ldap.reset()
self.mock_ldap.stop()

Besides of fixing return values for specific calls, you can also imitate a full ldap server with a directory of entries:

# Create an instance of MockLDAP with a preset directory
tree = {
   "cn=admin,dc=30loops,dc=net": {
      "userPassword": "ldaptest"
   }
}
mock_ldap = MockLDAP(tree)

record = [
   ('uid', 'crito'),
   ('userPassword', 'secret'),
]
# The
return value I expect when I add another record to the directory
eq_(
   (105, [], 1, []),
   mock_ldap.add_s("uid=crito,ou=people,dc=30loops,dc=net", record)
)

# The expected directory
directory = {
   "cn=admin,dc=30loops,dc=net": {
      "userPassword": "ldaptest"
   },
   "uid=crito,ou=people,dc=30loops,dc=net": {
      "uid": "crito",
      "userPassword": "secret"
   }
}
# Compare the expected directory with the MockLDAP directory
eq_(directory, mock_ldap.directory)

Suggestion : 3

For this tutorial, we will require Python 3 installed. Having it on our machine, let's set up a simple folder structure:

python - api - mocks / ├──users_test / └──test_users.py└── __init__.py├── __init__.py├── requirements.txt├── users.py

We will make use of virtualenv; a tool that enables us to create isolated Python environments. These environments help us to manage dependencies separately from the global packages directory. Let's first install virtualenv, then let's create a virtual environment for our project, and then let's activate it:

$ pip3 install virtualenv
$ virtualenv - p python3 venv # Create virtual environment
$ source venv / bin / activate # Activate virtual environment

After that, let's install the required packages:

$ pip install nose2 requests

TDD is an evolutionary approach to development that combines test-first development and refactoring. We write a test before we write just enough production code to fulfill that test. We then refactor the code to make the test pass. The main goal of TDD is the specification and not validation; it’s one way to think through our requirements before we write functional code. We will follow this approach and begin by writing a simple test to check our API's response's status code.

# users_test / test_users.py
import unittest
from users
import get_users

class BasicTests(unittest.TestCase):
   def test_request_response(self):
   response = get_users()

# Assert that the request - response cycle completed successfully with status code 200.
self.assertEqual(response.status_code, 200)

if __name__ == "__main__":
   unittest.main()

We then refactor the functionality to make it pass.

# users.py

import requests

USERS_URL = 'http://jsonplaceholder.typicode.com/users'

def get_users():
   ""
"Get list of users"
""
response = requests.get(USERS_URL)
if response.ok:
   return response
else:
   return None

Then use it in our endpoints:

# Controllers API

# This doesn 't need authentication
@app.route("/ping")
@cross_origin(headers = ['Content-Type', 'Authorization'])
def ping():
   return "All good. You don't need to be authenticated to call this"

# This does need authentication
@app.route("/secured/ping")
@cross_origin(headers = ['Content-Type', 'Authorization'])
@requires_auth
def secured_ping():
   return "All good. You only get this message if you're authenticated"