Skip to content

Commit 067efb6

Browse files
committed
Merge pull request #82 from switowski/ldap_cern
MiscUtil: Added CERN LDAP plugin
2 parents 7d7e5b7 + b014d7a commit 067efb6

3 files changed

Lines changed: 189 additions & 0 deletions

File tree

modules/miscutil/lib/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pylib_DATA = __init__.py \
3333
dbquery_regression_tests.py \
3434
dataciteutils.py \
3535
dataciteutils_tester.py \
36+
ldap_cern.py \
3637
logicutils.py \
3738
logicutils_unit_tests.py \
3839
mailutils.py \

modules/miscutil/lib/ldap_cern.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
## This file is part of Invenio.
2+
## Copyright (C) 2009, 2010, 2011, 2014 CERN.
3+
##
4+
## Invenio is free software; you can redistribute it and/or
5+
## modify it under the terms of the GNU General Public License as
6+
## published by the Free Software Foundation; either version 2 of the
7+
## License, or (at your option) any later version.
8+
##
9+
## Invenio is distributed in the hope that it will be useful, but
10+
## WITHOUT ANY WARRANTY; without even the implied warranty of
11+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
## General Public License for more details.
13+
##
14+
## You should have received a copy of the GNU General Public License
15+
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
16+
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17+
18+
"""Invenio LDAP interface for CERN. """
19+
20+
from time import sleep
21+
from thread import get_ident
22+
23+
import ldap
24+
import ldap.filter
25+
26+
CFG_CERN_LDAP_URI = "ldap://xldap.cern.ch:389"
27+
CFG_CERN_LDAP_BASE = "OU=Users,OU=Organic Units,DC=cern,DC=ch"
28+
29+
_ldap_connection_pool = {}
30+
31+
32+
def _cern_ldap_login():
33+
"""Get a connection from _ldap_connection_pool or create a new one"""
34+
try:
35+
connection = _ldap_connection_pool[get_ident()]
36+
except KeyError:
37+
connection = _ldap_connection_pool[get_ident()] = ldap.initialize(CFG_CERN_LDAP_URI)
38+
return connection
39+
40+
41+
def _sanitize_input(query):
42+
"""
43+
Take the query, filter it through ldap.filter.escape_filter_chars and
44+
replace the dots with spaces.
45+
"""
46+
query = ldap.filter.escape_filter_chars(query)
47+
query = query.replace(".", " ")
48+
return query
49+
50+
51+
def get_users_info_by_displayName(displayName):
52+
"""
53+
Query the CERN LDAP server for information about all users whose name
54+
contains the displayName.
55+
Return a list of user dictionaries (or empty list).
56+
"""
57+
58+
connection = _cern_ldap_login()
59+
60+
# Split displayName and add each part of it to the search query
61+
if displayName:
62+
query = _sanitize_input(displayName)
63+
query_elements = query.split()
64+
query_filter = "& "
65+
for element in query_elements:
66+
query_filter += '(displayName=*%s*) ' % element
67+
# Query will look like that: "(& (displayName=*john*) (displayName=*smith*)"
68+
# Eliminate the secondary accounts (aliases, etc.)
69+
query_filter = "(& (%s) (| (employeetype=primary) (employeetype=external) (employeetype=ExCern) ) )" % query_filter
70+
else:
71+
return []
72+
73+
try:
74+
results = connection.search_st(CFG_CERN_LDAP_BASE, ldap.SCOPE_SUBTREE,
75+
query_filter, timeout=5)
76+
except ldap.LDAPError:
77+
## Mmh.. connection error? Let's reconnect at least once just in case
78+
sleep(1)
79+
connection = _cern_ldap_login()
80+
try:
81+
results = connection.search_st(CFG_CERN_LDAP_BASE, ldap.SCOPE_SUBTREE,
82+
query_filter, timeout=5)
83+
except ldap.LDAPError:
84+
# Another error (maybe the LDAP query size is too big, etc.)
85+
# TODO, if it's needed, here we can return various different
86+
# information based on the error message
87+
results = []
88+
return results
89+
90+
91+
def get_users_info_by_displayName_or_email(name):
92+
"""
93+
Query the CERN LDAP server for information about all users whose displayName
94+
or email contains the name.
95+
Return a list of user dictionaries (or empty list).
96+
"""
97+
98+
connection = _cern_ldap_login()
99+
100+
# Split name and add each part of it to the search query
101+
if name:
102+
query = _sanitize_input(name)
103+
query_elements = query.split()
104+
query_filter_name = "& "
105+
query_filter_email = "& "
106+
for element in query_elements:
107+
query_filter_name += '(displayName=*%s*) ' % element
108+
query_filter_email += '(mail=*%s*) ' % element
109+
# query_filter_name will look like that:
110+
# "(| (& (displayName=*john*) (displayName=*smith*)) (& (mail=*john*) (mail=*smith*)) )"
111+
# Eliminate the secondary accounts (aliases, etc.)
112+
query_filter = "(& (| (%s) (%s)) (| (employeetype=primary) (employeetype=external) (employeetype=ExCern) ) )" % (query_filter_name, query_filter_email)
113+
else:
114+
return []
115+
116+
try:
117+
results = connection.search_st(CFG_CERN_LDAP_BASE, ldap.SCOPE_SUBTREE,
118+
query_filter, timeout=5)
119+
except ldap.LDAPError:
120+
## Mmh.. connection error? Let's reconnect at least once just in case
121+
sleep(1)
122+
connection = _cern_ldap_login()
123+
try:
124+
results = connection.search_st(CFG_CERN_LDAP_BASE, ldap.SCOPE_SUBTREE,
125+
query_filter, timeout=5)
126+
except ldap.LDAPError:
127+
# Another error (maybe the LDAP query size is too big, etc.)
128+
# TODO, if it's needed, here we can return various different
129+
# information based on the error message
130+
results = []
131+
return results
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# -*- coding: utf-8 -*-
2+
##
3+
## This file is part of Invenio.
4+
## Copyright (C) 2008, 2009, 2010, 2011, 2013, 2014 CERN.
5+
##
6+
## Invenio is free software; you can redistribute it and/or
7+
## modify it under the terms of the GNU General Public License as
8+
## published by the Free Software Foundation; either version 2 of the
9+
## License, or (at your option) any later version.
10+
##
11+
## Invenio is distributed in the hope that it will be useful, but
12+
## WITHOUT ANY WARRANTY; without even the implied warranty of
13+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
## General Public License for more details.
15+
##
16+
## You should have received a copy of the GNU General Public License
17+
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
18+
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19+
20+
"""Unit tests for the solrutils library."""
21+
22+
from invenio.testutils import InvenioTestCase
23+
from invenio.ldap_cern import (get_users_info_by_displayName,
24+
get_users_info_by_displayName_or_email)
25+
from invenio.testutils import make_test_suite, run_test_suite
26+
27+
28+
class TestLDAPGetUserInfo(InvenioTestCase):
29+
"""Test for retrieving users information from LDAP at CERN."""
30+
31+
def test_no_user(self):
32+
"""Try to get user that doesn't exists"""
33+
username = "John Nonexisting"
34+
expected_info = []
35+
self.assertEqual(get_users_info_by_displayName(username), expected_info)
36+
self.assertEqual(get_users_info_by_displayName_or_email(username), expected_info)
37+
38+
def test_single_user(self):
39+
"""Try to get a specific user (requires a user from CERN)."""
40+
username = "Tibor Simko"
41+
expected_results = 1
42+
expected_displayName = "Tibor Simko"
43+
expected_email = "Tibor.Simko@cern.ch"
44+
expected_affiliation = "CERN"
45+
ldap_info = get_users_info_by_displayName(username)
46+
ldap_info2 = get_users_info_by_displayName_or_email(username)
47+
48+
self.assertEqual(ldap_info, ldap_info2)
49+
self.assertEqual(len(ldap_info), expected_results)
50+
self.assertEqual(ldap_info[0][1].get('displayName', [])[0], expected_displayName)
51+
self.assertEqual(ldap_info[0][1].get('mail', [])[0], expected_email)
52+
self.assertEqual(ldap_info[0][1].get('cernInstituteName', [])[0], expected_affiliation)
53+
54+
TEST_SUITE = make_test_suite(TestLDAPGetUserInfo)
55+
56+
if __name__ == "__main__":
57+
run_test_suite(TEST_SUITE)

0 commit comments

Comments
 (0)