Skip to content

Commit

Permalink
Preliminary test setup and initial tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rlskoeser committed Dec 27, 2016
1 parent 6ec1018 commit 9f81b3f
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# local, project-specific
testsettings.py

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
9 changes: 9 additions & 0 deletions ci/testsettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# minimal django settings required to run tests
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "test.db",
}
}

SECRET_KEY = ''
16 changes: 13 additions & 3 deletions pucas/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def __init__(self):
# retrieve settings and initialize connection
ldap_servers = []
for server in settings.PUCAS_LDAP['SERVERS']:
ldap_servers = ldap3.Server(server, get_info=ldap3.ALL,
use_ssl=True)
ldap_servers.append(ldap3.Server(server, get_info=ldap3.ALL,
use_ssl=True))
server_pool = ldap3.ServerPool(ldap_servers,
ldap3.ROUND_ROBIN, active=True, exhaust=5)

Expand All @@ -35,6 +35,11 @@ def __init__(self):

def find_user(self, netid, all_attributes=False):
if netid:
# check for required settings and error if not available
required_configs = ['ATTRIBUTES', 'SEARCH_BASE', 'SEARCH_FILTER']
if any(req not in settings.PUCAS_LDAP for req in required_configs):
raise LDAPSearchException('LDAP is not configured for user lookup')

# for testing, to see all available attributes
if all_attributes:
search_attributes = '*'
Expand All @@ -61,7 +66,12 @@ def user_info_from_ldap(user):
'''Populate django user info from ldap'''

# configured mapping of user fields to ldap fields
attr_map = settings.PUCAS_LDAP['ATTRIBUTE_MAP']
attr_map = settings.PUCAS_LDAP['ATTRIBUTE_MAP', None]
# if no map is configured, nothing to do
if not attr_map:
# is logging sufficient here? or should it be an exception
logging.warn('No attribute map configured; not populating user info from ldap')
return

user_info = LDAPSearch().find_user(user.username)
if user_info:
Expand Down
125 changes: 125 additions & 0 deletions pucas/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from unittest import mock
from django.conf import settings
from django.test import TestCase, override_settings
from ldap3.core.exceptions import LDAPException
import pytest

from pucas.ldap import LDAPSearch, LDAPSearchException
from pucas.signals import cas_login


class TestSignals(TestCase):
# NOTE: using django TestCase for compatibility with
# django test runner

@mock.patch('pucas.signals.user_info_from_ldap')
def test_cas_login(self, mock_userinit):
mockuser = mock.Mock()
# if create is not true, user init method should be called
# (currently not using any args besides user and created)
cas_login(mock.Mock(), mockuser, False, {}, mock.Mock(), mock.Mock())
mock_userinit.assert_not_called()

# if create is true, user init method should be called
cas_login(mock.Mock(), mockuser, True, {}, mock.Mock(), mock.Mock())
mock_userinit.assert_called_with(mockuser)


class TestLDAPSearch(TestCase):

ldap_servers = ['lds81', 'ldap42', 'ld4all']

@mock.patch('pucas.ldap.ldap3')
@override_settings(PUCAS_LDAP={'SERVERS': ldap_servers})
def test_init(self, mockldap3):

# initialize and then check expected behavior against
# mock ldap3
LDAPSearch()

test_servers = []
for test_server in self.ldap_servers:
mockldap3.Server.assert_any_call(test_server,
get_info=mockldap3.ALL, use_ssl=True)

# initialized servers are collected into server pool
servers = [mockldap3.Server.return_value
for test_server in self.ldap_servers]
mockldap3.ServerPool.assert_called_with(servers,
mockldap3.ROUND_ROBIN, active=True, exhaust=5)

# server pool is used for connection
mockldap3.Connection.assert_called_with(mockldap3.ServerPool.return_value,
auto_bind=True)

with pytest.raises(LDAPException):
mockldap3.Connection.side_effect = LDAPException
LDAPSearch()

@mock.patch('pucas.ldap.ldap3')
@override_settings(PUCAS_LDAP={'SERVERS': ldap_servers,
'ATTRIBUTES': ['uid', 'sn', 'ou'],
'SEARCH_BASE': 'o=my_org', 'SEARCH_FILTER': "(uid=%(user)s)"})
def test_find_user(self, mockldap3):
ldsearch = LDAPSearch()

# empty netid should error
with pytest.raises(LDAPSearchException):
ldsearch.find_user(None)
with pytest.raises(LDAPSearchException):
ldsearch.find_user('')

netid = 'jschmoe'
# simulate no results
ldsearch.conn.entries = []

with pytest.raises(LDAPSearchException) as search_err:
ldsearch.find_user(netid)

assert 'No match found for %s' % netid in str(search_err)
# search should use configured values
ldsearch.conn.search.assert_called_with(settings.PUCAS_LDAP['SEARCH_BASE'],
settings.PUCAS_LDAP['SEARCH_FILTER'] % {'user': netid},
attributes=settings.PUCAS_LDAP['ATTRIBUTES'])

# simulate too many matches
ldsearch.conn.entries = [mock.Mock(), mock.Mock()]
with pytest.raises(LDAPSearchException) as search_err:
ldsearch.find_user(netid)

assert 'Found more than one entry for %s' % netid in str(search_err)

# simulate one match
userinfo = mock.Mock()
ldsearch.conn.entries = [userinfo]
assert ldsearch.find_user(netid) == userinfo

# search for all attributes
ldsearch.find_user(netid, all_attributes=True)
# should use '*' instead of configured attributes
ldsearch.conn.search.assert_called_with(settings.PUCAS_LDAP['SEARCH_BASE'],
settings.PUCAS_LDAP['SEARCH_FILTER'] % {'user': netid},
attributes='*')

# with missing configs in any combination
bad_settings = [
# nothing set
{},
# attributes only
{'ATTRIBUTES': ['foo']},
# search filter missing
{'ATTRIBUTES': ['foo'], 'SEARCH_BASE': 'u=foo'},
# search base missing
{'ATTRIBUTES': ['foo'], 'SEARCH_FILTER': '(uid=u)'},
]

for bad_cfg in bad_settings:

with override_settings(PUCAS_LDAP=bad_cfg):
with pytest.raises(LDAPSearchException) as search_err:
ldsearch.find_user(netid)
assert 'LDAP is not configured for user lookup' in str(search_err)




4 changes: 4 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[pytest]
DJANGO_SETTINGS_MODULE=testsettings
# look for tests in standard django test location
python_files = "**/tests.py"
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[aliases]
test=pytest
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
long_description=README,
url='https://github.com/Princeton-CDH/django-pucas',
install_requires=['django-cas-ng', 'ldap3'],
setup_requires=['pytest-runner'],
tests_requires=['pytest', 'pytest-django'],
author='CDH @ Princeton',
author_email='[email protected]',
classifiers=[
Expand Down

0 comments on commit 9f81b3f

Please sign in to comment.