|
| 1 | +# This file is part of Invenio. |
| 2 | +# Copyright (C) 2009, 2010, 2011, 2014, 2015, 2016 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 thread import get_ident |
| 21 | + |
| 22 | +import ldap |
| 23 | + |
| 24 | +from ldap.controls import SimplePagedResultsControl |
| 25 | + |
| 26 | + |
| 27 | +CFG_CERN_LDAP_URI = "ldap://xldap.cern.ch:389" |
| 28 | +CFG_CERN_LDAP_BASE = "OU=Users,OU=Organic Units,DC=cern,DC=ch" |
| 29 | +CFG_CERN_LDAP_PAGESIZE = 250 |
| 30 | + |
| 31 | +_ldap_connection_pool = {} |
| 32 | + |
| 33 | + |
| 34 | +class LDAPError(Exception): |
| 35 | + |
| 36 | + """Base class for exceptions in this module.""" |
| 37 | + |
| 38 | + pass |
| 39 | + |
| 40 | + |
| 41 | +def _cern_ldap_login(): |
| 42 | + """Get a connection from _ldap_connection_pool or create a new one.""" |
| 43 | + try: |
| 44 | + connection = _ldap_connection_pool[get_ident()] |
| 45 | + except KeyError: |
| 46 | + connection = _ldap_connection_pool[get_ident()] |
| 47 | + _ldap_connection_pool[get_ident()] = ldap.initialize(CFG_CERN_LDAP_URI) |
| 48 | + |
| 49 | + connection.set_option(ldap.OPT_PROTOCOL_VERSION, 3) |
| 50 | + return connection |
| 51 | + |
| 52 | + |
| 53 | +def _msgid(connection, req_ctrl, query_filter, attr_list=None): |
| 54 | + """Run the search request using search_ext. |
| 55 | +
|
| 56 | + :param string query_filter: filter to apply in the LDAP search |
| 57 | + :param list attr_list: retrieved LDAP attributes. If None, all attributes |
| 58 | + are returned |
| 59 | + :return: msgid |
| 60 | + """ |
| 61 | + try: |
| 62 | + return connection.search_ext( |
| 63 | + CFG_CERN_LDAP_BASE, |
| 64 | + ldap.SCOPE_SUBTREE, |
| 65 | + query_filter, |
| 66 | + attr_list, |
| 67 | + attrsonly=0, |
| 68 | + serverctrls=[req_ctrl]) |
| 69 | + except ldap.SERVER_DOWN as e: |
| 70 | + raise LDAPError("Error: Connection to CERN LDAP failed. ({0})" |
| 71 | + .format(e)) |
| 72 | + |
| 73 | + |
| 74 | +def _paged_search(connection, query_filter, attr_list=None): |
| 75 | + """Search the CERN LDAP server using pagination. |
| 76 | +
|
| 77 | + :param string query_filter: filter to apply in the LDAP search |
| 78 | + :param list attr_list: retrieved LDAP attributes. If None, all attributes |
| 79 | + are returned |
| 80 | + :return: list of tuples (result-type, result-data) or empty list, |
| 81 | + where result-data contains the user dictionary |
| 82 | + """ |
| 83 | + req_ctrl = SimplePagedResultsControl(True, CFG_CERN_LDAP_PAGESIZE, "") |
| 84 | + msgid = _msgid(connection, req_ctrl, query_filter, attr_list) |
| 85 | + result_pages = 0 |
| 86 | + results = [] |
| 87 | + |
| 88 | + while True: |
| 89 | + rtype, rdata, rmsgid, rctrls = connection.result3(msgid) |
| 90 | + results.extend(rdata) |
| 91 | + result_pages += 1 |
| 92 | + |
| 93 | + pctrls = [ |
| 94 | + c |
| 95 | + for c in rctrls |
| 96 | + if c.controlType == SimplePagedResultsControl.controlType |
| 97 | + ] |
| 98 | + if pctrls: |
| 99 | + if pctrls[0].cookie: |
| 100 | + req_ctrl.cookie = pctrls[0].cookie |
| 101 | + msgid = _msgid(connection, req_ctrl, |
| 102 | + query_filter, attr_list) |
| 103 | + else: |
| 104 | + break |
| 105 | + |
| 106 | + return results |
| 107 | + |
| 108 | + |
| 109 | +def get_users_records_data(query_filter, attr_list=None, decode_encoding=None): |
| 110 | + """Get result-data of records. |
| 111 | +
|
| 112 | + :param string query_filter: filter to apply in the LDAP search |
| 113 | + :param list attr_list: retrieved LDAP attributes. If None, all attributes |
| 114 | + are returned |
| 115 | + :param string decode_encoding: decode the values of the LDAP records |
| 116 | + :return: list of LDAP records, but result-data only |
| 117 | + """ |
| 118 | + connection = _cern_ldap_login() |
| 119 | + records = _paged_search(connection, query_filter, attr_list) |
| 120 | + |
| 121 | + records_data = [] |
| 122 | + |
| 123 | + if decode_encoding: |
| 124 | + records_data = [ |
| 125 | + dict( |
| 126 | + (k, [v[0].decode(decode_encoding)]) for (k, v) in x.iteritems() |
| 127 | + ) |
| 128 | + for (dummy, x) in records] |
| 129 | + else: |
| 130 | + records_data = [x for (dummy, x) in records] |
| 131 | + |
| 132 | + return records_data |
0 commit comments