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. """
1
+ # This file is part of Invenio.
2
+ # Copyright (C) 2009, 2010, 2011, 2014, 2015 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
19
20
- from time import sleep
21
20
from thread import get_ident
21
+ from time import sleep
22
22
23
23
import ldap
24
+ from ldap .controls import SimplePagedResultsControl
24
25
import ldap .filter
25
26
26
27
CFG_CERN_LDAP_URI = "ldap://xldap.cern.ch:389"
27
28
CFG_CERN_LDAP_BASE = "OU=Users,OU=Organic Units,DC=cern,DC=ch"
29
+ CFG_CERN_LDAP_PAGESIZE = 250
28
30
29
31
_ldap_connection_pool = {}
30
32
31
33
34
+ class LDAPError (Exception ):
35
+
36
+ """Base class for exceptions in this module."""
37
+
38
+ pass
39
+
40
+
32
41
def _cern_ldap_login ():
33
- """Get a connection from _ldap_connection_pool or create a new one"""
42
+ """Get a connection from _ldap_connection_pool or create a new one. """
34
43
try :
35
44
connection = _ldap_connection_pool [get_ident ()]
36
45
except KeyError :
37
46
connection = _ldap_connection_pool [get_ident ()] = ldap .initialize (CFG_CERN_LDAP_URI )
47
+
48
+ connection .set_option (ldap .OPT_PROTOCOL_VERSION , 3 )
38
49
return connection
39
50
40
51
41
52
def _sanitize_input (query ):
42
53
"""
43
- Take the query, filter it through ldap.filter.escape_filter_chars and
54
+ Sanitize input query.
55
+
56
+ Take the query, filter it through ldap.filter.escape_filter_chars, and
44
57
replace the dots with spaces.
45
58
"""
46
59
query = ldap .filter .escape_filter_chars (query )
47
60
query = query .replace ("." , " " )
48
61
return query
49
62
50
63
51
- def get_users_info_by_displayName (displayName ):
64
+ def _msgid (connection , req_ctrl , query_filter , attr_list = None ):
65
+ """Run the search request using search_ext.
66
+
67
+ :param string query_filter: filter to apply in the LDAP search
68
+ :param list attr_list: retrieved LDAP attributes. If None, all attributes
69
+ are returned
70
+ :return: msgid
71
+ """
72
+ try :
73
+ return connection .search_ext (
74
+ CFG_CERN_LDAP_BASE ,
75
+ ldap .SCOPE_SUBTREE ,
76
+ query_filter ,
77
+ attr_list ,
78
+ attrsonly = 0 ,
79
+ serverctrls = [req_ctrl ])
80
+ except ldap .SERVER_DOWN as e :
81
+ raise LDAPError ("Error: Connection to CERN LDAP failed. ({0})"
82
+ .format (e ))
83
+
84
+
85
+ def _paged_search (connection , query_filter , attr_list = None ):
86
+ """Search the CERN LDAP server using pagination.
87
+
88
+ :param string query_filter: filter to apply in the LDAP search
89
+ :param list attr_list: retrieved LDAP attributes. If None, all attributes
90
+ are returned
91
+ :return: list of tuples (result-type, result-data) or empty list,
92
+ where result-data contains the user dictionary
52
93
"""
94
+ req_ctrl = SimplePagedResultsControl (True , CFG_CERN_LDAP_PAGESIZE , "" )
95
+ msgid = _msgid (connection , req_ctrl , query_filter , attr_list )
96
+ result_pages = 0
97
+ results = []
98
+
99
+ while True :
100
+ rtype , rdata , rmsgid , rctrls = connection .result3 (msgid )
101
+ results .extend (rdata )
102
+ result_pages += 1
103
+
104
+ pctrls = [
105
+ c
106
+ for c in rctrls
107
+ if c .controlType == SimplePagedResultsControl .controlType
108
+ ]
109
+ if pctrls :
110
+ if pctrls [0 ].cookie :
111
+ req_ctrl .cookie = pctrls [0 ].cookie
112
+ msgid = _msgid (connection , req_ctrl ,
113
+ query_filter , attr_list )
114
+ else :
115
+ break
116
+
117
+ return results
118
+
119
+
120
+ def get_users_records_data (query_filter , attr_list = None , decode_encoding = None ):
121
+ """Get result-data of records.
122
+
123
+ :param string query_filter: filter to apply in the LDAP search
124
+ :param list attr_list: retrieved LDAP attributes. If None, all attributes
125
+ are returned
126
+ :param string decode_encoding: decode the values of the LDAP records
127
+ :return: list of LDAP records, but result-data only
128
+ """
129
+ connection = _cern_ldap_login ()
130
+ records = _paged_search (connection , query_filter , attr_list )
131
+
132
+ records_data = []
133
+
134
+ if decode_encoding :
135
+ records_data = [
136
+ dict (
137
+ (k , [v [0 ].decode (decode_encoding )]) for (k , v ) in x .iteritems ()
138
+ )
139
+ for (dummy , x ) in records ]
140
+ else :
141
+ records_data = [x for (dummy , x ) in records ]
142
+
143
+ return records_data
144
+
145
+
146
+ def get_users_info_by_displayName (displayName ):
147
+ """Get user information, given the displayName.
148
+
53
149
Query the CERN LDAP server for information about all users whose name
54
150
contains the displayName.
55
151
Return a list of user dictionaries (or empty list).
56
152
"""
57
-
58
153
connection = _cern_ldap_login ()
59
154
60
155
# Split displayName and add each part of it to the search query
@@ -64,22 +159,20 @@ def get_users_info_by_displayName(displayName):
64
159
query_filter = "& "
65
160
for element in query_elements :
66
161
query_filter += '(displayName=*%s*) ' % element
67
- # Query will look like that : "(& (displayName=*john*) (displayName=*smith*)"
162
+ # Query will look like: "(& (displayName=*john*) (displayName=*smith*)"
68
163
# Eliminate the secondary accounts (aliases, etc.)
69
164
query_filter = "(& (%s) (| (employeetype=primary) (employeetype=external) (employeetype=ExCern) ) )" % query_filter
70
165
else :
71
166
return []
72
167
73
168
try :
74
- results = connection .search_st (CFG_CERN_LDAP_BASE , ldap .SCOPE_SUBTREE ,
75
- query_filter , timeout = 5 )
169
+ results = _paged_search (connection , query_filter )
76
170
except ldap .LDAPError :
77
- ## Mmh.. connection error? Let's reconnect at least once just in case
171
+ # Mmh.. connection error? Let's reconnect at least once just in case
78
172
sleep (1 )
79
173
connection = _cern_ldap_login ()
80
174
try :
81
- results = connection .search_st (CFG_CERN_LDAP_BASE , ldap .SCOPE_SUBTREE ,
82
- query_filter , timeout = 5 )
175
+ results = _paged_search (connection , query_filter )
83
176
except ldap .LDAPError :
84
177
# Another error (maybe the LDAP query size is too big, etc.)
85
178
# TODO, if it's needed, here we can return various different
@@ -89,12 +182,12 @@ def get_users_info_by_displayName(displayName):
89
182
90
183
91
184
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.
185
+ """Get user information, given displayName or email address.
186
+
187
+ Query the CERN LDAP server for information about all users whose
188
+ displayName or email contains the name.
95
189
Return a list of user dictionaries (or empty list).
96
190
"""
97
-
98
191
connection = _cern_ldap_login ()
99
192
100
193
# Split name and add each part of it to the search query
@@ -114,15 +207,13 @@ def get_users_info_by_displayName_or_email(name):
114
207
return []
115
208
116
209
try :
117
- results = connection .search_st (CFG_CERN_LDAP_BASE , ldap .SCOPE_SUBTREE ,
118
- query_filter , timeout = 5 )
210
+ results = _paged_search (connection , query_filter )
119
211
except ldap .LDAPError :
120
- ## Mmh.. connection error? Let's reconnect at least once just in case
212
+ # Mmh.. connection error? Let's reconnect at least once just in case
121
213
sleep (1 )
122
214
connection = _cern_ldap_login ()
123
215
try :
124
- results = connection .search_st (CFG_CERN_LDAP_BASE , ldap .SCOPE_SUBTREE ,
125
- query_filter , timeout = 5 )
216
+ results = _paged_search (connection , query_filter )
126
217
except ldap .LDAPError :
127
218
# Another error (maybe the LDAP query size is too big, etc.)
128
219
# TODO, if it's needed, here we can return various different
0 commit comments