diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py index 3267502dc7..2da70e32a0 100644 --- a/keystone/common/ldap/core.py +++ b/keystone/common/ldap/core.py @@ -960,10 +960,24 @@ class KeystoneLDAPHandler(LDAPHandler): def _paged_search_s(self, base, scope, filterstr, attrlist=None): res = [] - lc = ldap.controls.SimplePagedResultsControl( - controlType=ldap.LDAP_CONTROL_PAGE_OID, - criticality=True, - controlValue=(self.page_size, '')) + use_old_paging_api = False + # The API for the simple paged results control changed between + # python-ldap 2.3 and 2.4. We need to detect the capabilities + # of the python-ldap version we are using. + if hasattr(ldap, 'LDAP_CONTROL_PAGE_OID'): + use_old_paging_api = True + lc = ldap.controls.SimplePagedResultsControl( + controlType=ldap.LDAP_CONTROL_PAGE_OID, + criticality=True, + controlValue=(self.page_size, '')) + page_ctrl_oid = ldap.LDAP_CONTROL_PAGE_OID + else: + lc = ldap.controls.libldap.SimplePagedResultsControl( + criticality=True, + size=self.page_size, + cookie='') + page_ctrl_oid = ldap.controls.SimplePagedResultsControl.controlType + base_utf8 = utf8_encode(base) filterstr_utf8 = utf8_encode(filterstr) if attrlist is None: @@ -983,14 +997,18 @@ class KeystoneLDAPHandler(LDAPHandler): # Receive the data res.extend(rdata) pctrls = [c for c in serverctrls - if c.controlType == ldap.LDAP_CONTROL_PAGE_OID] + if c.controlType == page_ctrl_oid] if pctrls: # LDAP server supports pagination - est, cookie = pctrls[0].controlValue + if use_old_paging_api: + est, cookie = pctrls[0].controlValue + lc.controlValue = (self.page_size, cookie) + else: + cookie = lc.cookie = pctrls[0].cookie + if cookie: # There is more data still on the server # so we request another page - lc.controlValue = (self.page_size, cookie) msgid = self.conn.search_ext(base_utf8, scope, filterstr_utf8, diff --git a/keystone/tests/unit/common/test_ldap.py b/keystone/tests/unit/common/test_ldap.py index 61f2fd1463..b3e70c97b3 100644 --- a/keystone/tests/unit/common/test_ldap.py +++ b/keystone/tests/unit/common/test_ldap.py @@ -349,3 +349,45 @@ class SslTlsTest(tests.TestCase): # Ensure the cert trust option is set. self.assertEqual(certdir, ldap.get_option(ldap.OPT_X_TLS_CACERTDIR)) + + +class LDAPPagedResultsTest(tests.TestCase): + """Tests the paged results functionality in keystone.common.ldap.core.""" + + def setUp(self): + super(LDAPPagedResultsTest, self).setUp() + self.clear_database() + + ks_ldap.register_handler('fake://', fakeldap.FakeLdap) + self.addCleanup(common_ldap_core._HANDLERS.clear) + + self.load_backends() + self.load_fixtures(default_fixtures) + + def clear_database(self): + for shelf in fakeldap.FakeShelves: + fakeldap.FakeShelves[shelf].clear() + + def config_overrides(self): + super(LDAPPagedResultsTest, self).config_overrides() + self.config_fixture.config( + group='identity', + driver='keystone.identity.backends.ldap.Identity') + + def config_files(self): + config_files = super(LDAPPagedResultsTest, self).config_files() + config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + return config_files + + @mock.patch.object(fakeldap.FakeLdap, 'search_ext') + @mock.patch.object(fakeldap.FakeLdap, 'result3') + def test_paged_results_control_api(self, mock_result3, mock_search_ext): + mock_result3.return_value = ('', [], 1, []) + + self.config_fixture.config(group='ldap', + page_size=1) + + conn = self.identity_api.user.get_connection() + conn._paged_search_s('dc=example,dc=test', + ldap.SCOPE_SUBTREE, + 'objectclass=*')