Increase LDAP coverage.

This commit is contained in:
Antoine Nguyen
2016-05-04 12:10:35 +02:00
parent 2f5c1dfb04
commit 74266e64ce
3 changed files with 114 additions and 31 deletions
+18
View File
@@ -3,6 +3,8 @@
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.core import tests as core_tests
from modoboa.lib import parameters
from modoboa.lib.tests import ModoTestCase
from .. import factories
@@ -152,6 +154,22 @@ class AccountTestCase(ModoTestCase):
)
class LDAPAccountTestCase(core_tests.LDAPTestCaseMixin, ModoTestCase):
"""Check LDAP related code."""
def test_autocreate_disabled(self):
"""Check if objects are not created as expected."""
self.activate_ldap_authentication()
self.searchbind_mode()
parameters.save_admin("AUTO_CREATE_DOMAIN_AND_MAILBOX", "no")
username = "testuser@example.com"
self.authenticate(username, "test")
self.assertFalse(
models.Domain.objects.filter(name="example.com").exists())
self.assertFalse(
models.Mailbox.objects.filter(address="testuser").exists())
class PermissionsTestCase(ModoTestCase):
@classmethod
+75 -18
View File
@@ -3,6 +3,7 @@
from django.core.urlresolvers import reverse
from django.test import override_settings
from modoboa.lib import exceptions
from modoboa.lib import parameters
from modoboa.lib.tests import ModoTestCase
@@ -39,20 +40,65 @@ class AuthenticationTestCase(ModoTestCase):
self.assertTrue(response.url.endswith(reverse("admin:domain_list")))
class LDAPTestCaseMixin(object):
"""Set of methods used to test LDAP features."""
def activate_ldap_authentication(self):
"""Modify settings."""
parameters.save_admin(
"AUTHENTICATION_TYPE", "ldap")
parameters.save_admin("LDAP_SERVER_PORT", "3389")
def restore_user_password(self, username, new_password):
"""Restore user password to its initial state."""
from modoboa.lib.ldap_utils import LDAPAuthBackend
backend = LDAPAuthBackend()
for password in ["Toto1234", "test"]:
try:
backend.update_user_password(
username, password, new_password)
except exceptions.InternalError as exp:
print exp
pass
else:
return
raise RuntimeError("Can't restore user password.")
def authenticate(self, user, password, restore_before=True):
"""Restore password and authenticate user."""
self.client.logout()
if restore_before:
self.restore_user_password(user, password)
self.assertTrue(
self.client.login(username=user, password=password))
def searchbind_mode(self):
"""Apply settings required by the searchbind mode."""
parameters.save_admin("LDAP_AUTH_METHOD", "searchbind")
parameters.save_admin("LDAP_BIND_DN", "cn=admin,dc=example,dc=com")
parameters.save_admin("LDAP_BIND_PASSWORD", "test")
parameters.save_admin("LDAP_SEARCH_BASE", "ou=users,dc=example,dc=com")
def directbind_mode(self):
"""Apply settings required by the directbind mode."""
parameters.save_admin("LDAP_AUTH_METHOD", "directbind")
parameters.save_admin(
"LDAP_USER_DN_TEMPLATE", "cn=%(user)s,ou=users,dc=example,dc=com")
@override_settings(AUTHENTICATION_BACKENDS=(
'modoboa.lib.authbackends.LDAPBackend',
'modoboa.lib.authbackends.SimpleBackend',
))
class LDAPAuthenticationTestCase(ModoTestCase):
class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase):
"""Validate LDAP authentication scenarios."""
def setUp(self):
"""Create test data."""
super(LDAPAuthenticationTestCase, self).setUp()
parameters.save_admin(
"AUTHENTICATION_TYPE", "ldap")
parameters.save_admin("LDAP_SERVER_PORT", "3389")
self.activate_ldap_authentication()
def check_created_user(self, username, group="SimpleUsers", with_mb=True):
"""Check that created user is valid."""
@@ -66,13 +112,9 @@ class LDAPAuthenticationTestCase(ModoTestCase):
@override_settings(AUTH_LDAP_USER_DN_TEMPLATE=None)
def test_searchbind_authentication(self):
"""Test the bind&search method."""
self.client.logout()
parameters.save_admin("LDAP_AUTH_METHOD", "searchbind")
parameters.save_admin("LDAP_BIND_DN", "cn=admin,dc=example,dc=com")
parameters.save_admin("LDAP_BIND_PASSWORD", "test")
parameters.save_admin("LDAP_SEARCH_BASE", "ou=users,dc=example,dc=com")
self.searchbind_mode()
username = "testuser@example.com"
self.assertTrue(self.client.login(username=username, password="test"))
self.authenticate(username, "test")
self.check_created_user(username)
self.client.logout()
@@ -80,22 +122,19 @@ class LDAPAuthenticationTestCase(ModoTestCase):
parameters.save_admin(
"LDAP_GROUPS_SEARCH_BASE", "ou=groups,dc=example,dc=com")
username = "mailadmin@example.com"
self.assertTrue(self.client.login(username=username, password="test"))
self.authenticate(username, "test", False)
self.check_created_user(username, "DomainAdmins")
def test_directbind_authentication(self):
"""Test the directbind method."""
self.client.logout()
parameters.save_admin("LDAP_AUTH_METHOD", "directbind")
parameters.save_admin(
"LDAP_USER_DN_TEMPLATE", "cn=%(user)s,ou=users,dc=example,dc=com")
self.directbind_mode()
# 1: must fail because usernames of simple users must be email
# addresses
username = "testuser"
with self.assertRaises(TypeError):
self.assertTrue(self.client.login(
username=username, password="test"))
self.client.login(username=username, password="test")
# 1: must work because usernames of domain admins are not
# always email addresses
@@ -103,11 +142,11 @@ class LDAPAuthenticationTestCase(ModoTestCase):
parameters.save_admin(
"LDAP_GROUPS_SEARCH_BASE", "ou=groups,dc=example,dc=com")
username = "mailadmin"
self.assertTrue(self.client.login(username=username, password="test"))
self.authenticate(username, "test", False)
self.check_created_user(username, "DomainAdmins", False)
class ProfileTestCase(ModoTestCase):
class ProfileTestCase(LDAPTestCaseMixin, ModoTestCase):
@classmethod
def setUpTestData(cls):
@@ -153,6 +192,24 @@ class ProfileTestCase(ModoTestCase):
self.client.login(username="user@test.com", password="Toto1234")
)
@override_settings(AUTHENTICATION_BACKENDS=(
'modoboa.lib.authbackends.LDAPBackend',
'modoboa.lib.authbackends.SimpleBackend',
))
def test_update_password_ldap(self):
"""Update password for an LDAP user."""
self.activate_ldap_authentication()
self.searchbind_mode()
username = "testuser@example.com"
self.authenticate(username, "test")
self.ajax_post(
reverse("core:user_profile"),
{"language": "en", "oldpassword": "test",
"newpassword": "Toto1234", "confirmation": "Toto1234"}
)
self.authenticate(username, "Toto1234", False)
class APIAccessFormTestCase(ModoTestCase):
+21 -13
View File
@@ -46,6 +46,7 @@ from modoboa.lib.exceptions import InternalError
class LDAPAuthBackend(object):
def __init__(self):
parameters.apply_to_django_settings()
self.server_uri = self._setting(
"AUTH_LDAP_SERVER_URI", "ldap://localhost"
)
@@ -53,7 +54,6 @@ class LDAPAuthBackend(object):
self.ldap_ad = self._setting("LDAP_ACTIVE_DIRECTORY", False)
self.conn = None
self.user_filter = self._setting("LDAP_USER_FILTER", "")
self.user_dn = None
def _setting(self, name, default):
try:
@@ -63,6 +63,7 @@ class LDAPAuthBackend(object):
return value
def _get_conn(self, dn, password):
"""Get a connection from the server."""
conn = ldap.initialize(self.server_uri)
conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
conn.set_option(ldap.OPT_DEBUG_LEVEL, 255)
@@ -70,17 +71,21 @@ class LDAPAuthBackend(object):
return conn
def connect_to_server(self, user, password):
if parameters.get_admin("LDAP_AUTH_METHOD", app="core") == "searchbind":
"""Connect to the server according to configuration."""
if self.conn is not None:
return
mode = parameters.get_admin("LDAP_AUTH_METHOD", app="core")
if mode == "searchbind":
bind_dn = self._setting("AUTH_LDAP_BIND_DN", "")
bind_pwd = self._setting("AUTH_LDAP_BIND_PASSWORD", "")
self.conn = self._get_conn(bind_dn, bind_pwd)
self.user_dn = self._find_user_dn(user)
else:
tpl = self._setting("AUTH_LDAP_USER_DN_TEMPLATE", "")
self.user_dn = tpl % {"user": user}
self.conn = self._get_conn(self.user_dn, password)
def _find_user_dn(self, user):
"""Find the DN of the given user."""
sbase = parameters.get_admin("LDAP_SEARCH_BASE", app="core")
sfilter = parameters.get_admin("LDAP_SEARCH_FILTER", app="core")
sfilter = sfilter % {"user": user}
@@ -102,15 +107,18 @@ class LDAPAuthBackend(object):
return hasher.encrypt(clearpassword)
def update_user_password(self, user, password, newpassword):
"""Update user password."""
self.connect_to_server(user, password)
user_dn = self._find_user_dn(user)
if self.ldap_ad:
newpassword = (
('"%s"' % newpassword).encode('utf-16').lstrip('\377\376')
)
ldif = [(ldap.MOD_REPLACE,
self.pwd_attr,
self._crypt_password(newpassword))]
try:
self.connect_to_server(user, password)
if self.ldap_ad:
newpassword = (
('"%s"' % newpassword).encode('utf-16').lstrip('\377\376')
)
ldif = [(ldap.MOD_REPLACE,
self.pwd_attr,
self._crypt_password(newpassword))]
self.conn.modify_s(self.user_dn, ldif)
self.conn.modify_s(user_dn, ldif)
except ldap.LDAPError as e:
raise InternalError(_("Failed to update password: %s" % str(e)))
raise InternalError(
_("Failed to update password: {}").format(e))