From 53cecf3abcc08724ae9b21fd02b8423fd39e7086 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Fri, 17 Nov 2023 14:41:51 +0100
Subject: [PATCH] Issue 5984 - Crash when paged result search are abandoned -
fix + fix2 (#5985 and #5987)
Notice: This cherry-pick include two commit:
df7dd8320 Issue 5984 - Crash when paged result search are abandoned - fix2 (#5987)
06bd08629 Issue 5984 - Crash when paged result search are abandoned (#5985)
The reason is that cherry pick of #5985 generates lots of conflict in __init.py
and #5987 only revert that file ==> So it is easier and safer to keep the original
file.
* Issue 5984 - Crash when paged result search are abandoned
Problem:
Fix #4551 has changed the lock that protects the paged result data
within a connection. But the abandon operation attempts to free
the paged search result with the connection lock.
This leads to race condition and double free causing an heap
corruption and a SIGSEGV.
Solution:
- Get a copy of the operation data that needs to be logged.
- Unlock the connection mutex (to avoid deadlock risk)
- Free the paged result while holding the paged result lock.
Issue: 5984
Reviewed by: @tbordaz (Thanks!)
(cherry picked from commit 06bd0862956672eb76276cab5c1dd906fe5a7eec)
---
.../paged_results/paged_results_test.py | 758 +++++++++---------
ldap/servers/slapd/abandon.c | 23 +-
ldap/servers/slapd/opshared.c | 4 +-
ldap/servers/slapd/pagedresults.c | 8 +-
ldap/servers/slapd/proto-slap.h | 2 +-
5 files changed, 392 insertions(+), 403 deletions(-)
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
index ae2627b75..a030824c6 100644
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
@@ -1,21 +1,32 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2016 Red Hat, Inc.
+# Copyright (C) 2020 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
-from random import sample
+import socket
+from random import sample, randrange
import pytest
from ldap.controls import SimplePagedResultsControl, GetEffectiveRightsControl
from lib389.tasks import *
from lib389.utils import *
from lib389.topologies import topology_st
-from lib389._constants import DN_LDBM, DN_DM, DEFAULT_SUFFIX, BACKEND_NAME, PASSWORD
+from lib389._constants import DN_LDBM, DN_DM, DEFAULT_SUFFIX
+from lib389._controls import SSSRequestControl
+from lib389.idm.user import UserAccount, UserAccounts
+from lib389.cli_base import FakeArgs
+from lib389.config import LDBMConfig
+from lib389.dbgen import dbgen_users
-from sss_control import SSSRequestControl
+from lib389.idm.organization import Organization
+from lib389.idm.organizationalunit import OrganizationalUnit
+from lib389.backend import Backends
+from lib389._mapped_object import DSLdapObject
+
+pytestmark = pytest.mark.tier1
DEBUGGING = os.getenv('DEBUGGING', False)
@@ -26,9 +37,8 @@ else:
log = logging.getLogger(__name__)
-TEST_USER_NAME = 'simplepaged_test'
-TEST_USER_DN = 'uid={},{}'.format(TEST_USER_NAME, DEFAULT_SUFFIX)
TEST_USER_PWD = 'simplepaged_test'
+
NEW_SUFFIX_1_NAME = 'test_parent'
NEW_SUFFIX_1 = 'o={}'.format(NEW_SUFFIX_1_NAME)
NEW_SUFFIX_2_NAME = 'child'
@@ -36,34 +46,90 @@ NEW_SUFFIX_2 = 'ou={},{}'.format(NEW_SUFFIX_2_NAME, NEW_SUFFIX_1)
NEW_BACKEND_1 = 'parent_base'
NEW_BACKEND_2 = 'child_base'
+OLD_HOSTNAME = socket.gethostname()
+if os.getuid() == 0:
+ socket.sethostname('localhost')
+HOSTNAME = socket.gethostname()
+IP_ADDRESS = socket.gethostbyname(HOSTNAME)
+OLD_IP_ADDRESS = socket.gethostbyname(OLD_HOSTNAME)
+
@pytest.fixture(scope="module")
-def test_user(topology_st, request):
+def create_40k_users(topology_st, request):
+ inst = topology_st.standalone
+
+ # Prepare return value
+ retval = FakeArgs()
+ retval.inst = inst
+ retval.bename = '40k'
+ retval.suffix = 'o=%s' % retval.bename
+ ldifdir = inst.get_ldif_dir()
+ retval.ldif_file = '%s/%s.ldif' % (ldifdir, retval.bename)
+
+ # Create new backend
+ bes = Backends(inst)
+ be_1 = bes.create(properties={
+ 'cn': retval.bename,
+ 'nsslapd-suffix': retval.suffix,
+ })
+
+ # Set paged search lookthrough limit
+ ldbmconfig = LDBMConfig(inst)
+ ldbmconfig.replace('nsslapd-pagedlookthroughlimit', b'100000')
+
+ # Create ldif and import it.
+ dbgen_users(inst, 40000, retval.ldif_file, retval.suffix)
+ # tasks = Tasks(inst)
+ # args = {TASK_WAIT: True}
+ # tasks.importLDIF(retval.suffix, None, retval.ldif_file, args)
+ inst.stop()
+ assert inst.ldif2db(retval.bename, None, None, None, retval.ldif_file, None)
+ inst.start()
+
+ # And set an aci allowing anonymous read
+ log.info('Adding ACI to allow our test user to search')
+ ACI_TARGET = '(targetattr != "userPassword || aci")'
+ ACI_ALLOW = '(version 3.0; acl "Enable anonymous access";allow (read, search, compare)'
+ ACI_SUBJECT = '(userdn = "ldap:///anyone");)'
+ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
+ o_1 = Organization(inst, retval.suffix)
+ o_1.set('aci', ACI_BODY)
+
+ return retval
+
+
+@pytest.fixture(scope="module")
+def create_user(topology_st, request):
"""User for binding operation"""
- log.info('Adding user {}'.format(TEST_USER_DN))
- try:
- topology_st.standalone.add_s(Entry((TEST_USER_DN, {
- 'objectclass': 'top person'.split(),
- 'objectclass': 'organizationalPerson',
- 'objectclass': 'inetorgperson',
- 'cn': TEST_USER_NAME,
- 'sn': TEST_USER_NAME,
- 'userpassword': TEST_USER_PWD,
- 'mail': '%s@redhat.com' % TEST_USER_NAME,
- 'uid': TEST_USER_NAME
- })))
- except ldap.LDAPError as e:
- log.error('Failed to add user (%s): error (%s)' % (TEST_USER_DN,
- e.message['desc']))
- raise e
+ log.info('Adding user simplepaged_test')
+ new_uri = topology_st.standalone.ldapuri.replace(OLD_HOSTNAME, HOSTNAME)
+ topology_st.standalone.ldapuri = new_uri
+ users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
+ user = users.create(properties={
+ 'uid': 'simplepaged_test',
+ 'cn': 'simplepaged_test',
+ 'sn': 'simplepaged_test',
+ 'uidNumber': '1234',
+ 'gidNumber': '1234',
+ 'homeDirectory': '/home/simplepaged_test',
+ 'userPassword': TEST_USER_PWD,
+ })
+
+ # Now add the ACI so simplepage_test can read the users ...
+ ACI_BODY = ensure_bytes('(targetattr= "uid || sn || dn")(version 3.0; acl "Allow read for user"; allow (read,search,compare) userdn = "ldap:///all";)')
+ topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_REPLACE, 'aci', ACI_BODY)])
def fin():
- log.info('Deleting user {}'.format(TEST_USER_DN))
- topology_st.standalone.delete_s(TEST_USER_DN)
+ log.info('Deleting user simplepaged_test')
+ if not DEBUGGING:
+ user.delete()
+ if os.getuid() == 0:
+ socket.sethostname(OLD_HOSTNAME)
request.addfinalizer(fin)
+ return user
@pytest.fixture(scope="module")
def new_suffixes(topology_st):
@@ -72,47 +138,40 @@ def new_suffixes(topology_st):
"""
log.info('Adding suffix:{} and backend: {}'.format(NEW_SUFFIX_1, NEW_BACKEND_1))
- topology_st.standalone.backend.create(NEW_SUFFIX_1,
- {BACKEND_NAME: NEW_BACKEND_1})
- topology_st.standalone.mappingtree.create(NEW_SUFFIX_1,
- bename=NEW_BACKEND_1)
- try:
- topology_st.standalone.add_s(Entry((NEW_SUFFIX_1, {
- 'objectclass': 'top',
- 'objectclass': 'organization',
- 'o': NEW_SUFFIX_1_NAME
- })))
- except ldap.LDAPError as e:
- log.error('Failed to add suffix ({}): error ({})'.format(NEW_SUFFIX_1,
- e.message['desc']))
- raise
- log.info('Adding suffix:{} and backend: {}'.format(NEW_SUFFIX_2, NEW_BACKEND_2))
- topology_st.standalone.backend.create(NEW_SUFFIX_2,
- {BACKEND_NAME: NEW_BACKEND_2})
- topology_st.standalone.mappingtree.create(NEW_SUFFIX_2,
- bename=NEW_BACKEND_2,
- parent=NEW_SUFFIX_1)
-
- try:
- topology_st.standalone.add_s(Entry((NEW_SUFFIX_2, {
- 'objectclass': 'top',
- 'objectclass': 'organizationalunit',
- 'ou': NEW_SUFFIX_2_NAME
- })))
- except ldap.LDAPError as e:
- log.error('Failed to add suffix ({}): error ({})'.format(NEW_SUFFIX_2,
- e.message['desc']))
- raise
+ bes = Backends(topology_st.standalone)
+ bes.create(properties={
+ 'cn': 'NEW_BACKEND_1',
+ 'nsslapd-suffix': NEW_SUFFIX_1,
+ })
+ # Create the root objects with their ACI
log.info('Adding ACI to allow our test user to search')
ACI_TARGET = '(targetattr != "userPassword || aci")'
ACI_ALLOW = '(version 3.0; acl "Enable anonymous access";allow (read, search, compare)'
ACI_SUBJECT = '(userdn = "ldap:///anyone");)'
ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
- mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
- topology_st.standalone.modify_s(NEW_SUFFIX_1, mod)
+ o_1 = Organization(topology_st.standalone, NEW_SUFFIX_1)
+ o_1.create(properties={
+ 'o': NEW_SUFFIX_1_NAME,
+ 'aci': ACI_BODY,
+ })
+
+ log.info('Adding suffix:{} and backend: {}'.format(NEW_SUFFIX_2, NEW_BACKEND_2))
+ be_2 = bes.create(properties={
+ 'cn': 'NEW_BACKEND_2',
+ 'nsslapd-suffix': NEW_SUFFIX_2,
+ })
+
+ # We have to adjust the MT to say that BE_1 is a parent.
+ mt = be_2.get_mapping_tree()
+ mt.set_parent(NEW_SUFFIX_1)
+
+ ou_2 = OrganizationalUnit(topology_st.standalone, NEW_SUFFIX_2)
+ ou_2.create(properties={
+ 'ou': NEW_SUFFIX_2_NAME
+ })
def add_users(topology_st, users_num, suffix):
@@ -122,72 +181,54 @@ def add_users(topology_st, users_num, suffix):
"""
users_list = []
+ users = UserAccounts(topology_st.standalone, suffix, rdn=None)
+
log.info('Adding %d users' % users_num)
for num in sample(range(1000), users_num):
num_ran = int(round(num))
USER_NAME = 'test%05d' % num_ran
- USER_DN = 'uid=%s,%s' % (USER_NAME, suffix)
- users_list.append(USER_DN)
- try:
- topology_st.standalone.add_s(Entry((USER_DN, {
- 'objectclass': 'top person'.split(),
- 'objectclass': 'organizationalPerson',
- 'objectclass': 'inetorgperson',
- 'cn': USER_NAME,
- 'sn': USER_NAME,
- 'userpassword': 'pass%s' % num_ran,
- 'mail': '%s@redhat.com' % USER_NAME,
- 'uid': USER_NAME})))
- except ldap.LDAPError as e:
- log.error('Failed to add user (%s): error (%s)' % (USER_DN,
- e.message['desc']))
- raise e
+
+ user = users.create(properties={
+ 'uid': USER_NAME,
+ 'sn': USER_NAME,
+ 'cn': USER_NAME,
+ 'uidNumber': '%s' % num_ran,
+ 'gidNumber': '%s' % num_ran,
+ 'homeDirectory': '/home/%s' % USER_NAME,
+ 'mail': '%s@redhat.com' % USER_NAME,
+ 'userpassword': 'pass%s' % num_ran,
+ })
+ users_list.append(user)
return users_list
-def del_users(topology_st, users_list):
+def del_users(users_list):
"""Delete users with DNs from given list"""
log.info('Deleting %d users' % len(users_list))
- for user_dn in users_list:
- try:
- topology_st.standalone.delete_s(user_dn)
- except ldap.LDAPError as e:
- log.error('Failed to delete user (%s): error (%s)' % (user_dn,
- e.message['desc']))
- raise e
+ for user in users_list:
+ user.delete()
def change_conf_attr(topology_st, suffix, attr_name, attr_value):
- """Change configurational attribute in the given suffix.
+ """Change configuration attribute in the given suffix.
Returns previous attribute value.
"""
- try:
- entries = topology_st.standalone.search_s(suffix, ldap.SCOPE_BASE,
- 'objectclass=top',
- [attr_name])
- attr_value_bck = entries[0].data.get(attr_name)
- log.info('Set %s to %s. Previous value - %s. Modified suffix - %s.' % (
- attr_name, attr_value, attr_value_bck, suffix))
- if attr_value is None:
- topology_st.standalone.modify_s(suffix, [(ldap.MOD_DELETE,
- attr_name,
- attr_value)])
- else:
- topology_st.standalone.modify_s(suffix, [(ldap.MOD_REPLACE,
- attr_name,
- attr_value)])
- except ldap.LDAPError as e:
- log.error('Failed to change attr value (%s): error (%s)' % (attr_name,
- e.message['desc']))
- raise e
+ entry = DSLdapObject(topology_st.standalone, suffix)
+ attr_value_bck = entry.get_attr_val_bytes(attr_name)
+ log.info('Set %s to %s. Previous value - %s. Modified suffix - %s.' % (
+ attr_name, attr_value, attr_value_bck, suffix))
+ if attr_value is None:
+ entry.remove_all(attr_name)
+ else:
+ entry.replace(attr_name, attr_value)
return attr_value_bck
-def paged_search(topology_st, suffix, controls, search_flt, searchreq_attrlist):
+def paged_search(conn, suffix, controls, search_flt, searchreq_attrlist, abandon_rate=0):
"""Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
using Simple Paged Control(should the first item in the
list controls.
@@ -206,14 +247,17 @@ def paged_search(topology_st, suffix, controls, search_flt, searchreq_attrlist):
searchreq_attrlist,
req_pr_ctrl.size,
str(controls)))
- msgid = topology_st.standalone.search_ext(suffix,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(suffix, ldap.SCOPE_SUBTREE, search_flt, searchreq_attrlist, serverctrls=controls)
+ log.info('Getting page %d' % (pages,))
while True:
- log.info('Getting page %d' % (pages,))
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ try:
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid, timeout=0.001)
+ except ldap.TIMEOUT:
+ if pages > 0 and abandon_rate>0 and randrange(100)<abandon_rate:
+ conn.abandon(msgid)
+ log.info('Paged result search is abandonned.')
+ return all_results
+ continue
log.debug('Data: {}'.format(rdata))
all_results.extend(rdata)
pages += 1
@@ -228,27 +272,25 @@ def paged_search(topology_st, suffix, controls, search_flt, searchreq_attrlist):
# Copy cookie from response control to request control
log.debug('Cookie: {}'.format(pctrls[0].cookie))
req_pr_ctrl.cookie = pctrls[0].cookie
- msgid = topology_st.standalone.search_ext(suffix,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(suffix, ldap.SCOPE_SUBTREE, search_flt, searchreq_attrlist, serverctrls=controls)
else:
break # No more pages available
else:
break
+ log.info('Getting page %d' % (pages,))
assert not pctrls[0].cookie
return all_results
-@pytest.mark.parametrize("page_size,users_num",
- [(6, 5), (5, 5), (5, 25)])
-def test_search_success(topology_st, test_user, page_size, users_num):
+@pytest.mark.parametrize("page_size,users_num", [(6, 5), (5, 5), (5, 25)])
+def test_search_success(topology_st, create_user, page_size, users_num):
"""Verify that search with a simple paged results control
returns all entries it should without errors.
:id: ddd15b70-64f1-4a85-a793-b24761e50354
+ :customerscenario: True
+ :parametrized: yes
:feature: Simple paged results
:setup: Standalone instance, test user for binding,
varying number of users for the search base
@@ -264,21 +306,16 @@ def test_search_success(topology_st, test_user, page_size, users_num):
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
- try:
- log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ log.info('Set user bind %s ' % create_user)
+ conn = create_user.bind(TEST_USER_PWD)
- req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ all_results = paged_search(conn, DEFAULT_SUFFIX, [req_ctrl], search_flt, searchreq_attrlist)
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, [req_ctrl],
- search_flt, searchreq_attrlist)
+ log.info('%d results' % len(all_results))
+ assert len(all_results) == len(users_list)
- log.info('%d results' % len(all_results))
- assert len(all_results) == len(users_list)
- finally:
- log.info('Set Directory Manager bind back (test_search_success)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
@pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err", [
@@ -292,13 +329,15 @@ def test_search_success(topology_st, test_user, page_size, users_num):
ldap.SIZELIMIT_EXCEEDED),
(5, 50, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', '20',
ldap.ADMINLIMIT_EXCEEDED)])
-def test_search_limits_fail(topology_st, test_user, page_size, users_num,
+def test_search_limits_fail(topology_st, create_user, page_size, users_num,
suffix, attr_name, attr_value, expected_err):
"""Verify that search with a simple paged results control
throws expected exceptoins when corresponding limits are
exceeded.
:id: e3067107-bd6d-493d-9989-3e641a9337b0
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -321,7 +360,7 @@ def test_search_limits_fail(topology_st, test_user, page_size, users_num,
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
@@ -330,11 +369,8 @@ def test_search_limits_fail(topology_st, test_user, page_size, users_num,
sort_ctrl = SSSRequestControl(True, ['sn'])
controls.append(sort_ctrl)
log.info('Initiate ldapsearch with created control instance')
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
time_val = conf_param_dict.get('nsslapd-timelimit')
if time_val:
@@ -345,12 +381,11 @@ def test_search_limits_fail(topology_st, test_user, page_size, users_num,
pctrls = []
while True:
log.info('Getting page %d' % (pages,))
- if pages == 0 and (time_val or attr_name in ('nsslapd-lookthroughlimit',
- 'nsslapd-pagesizelimit')):
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ if pages == 0 and (time_val or attr_name == 'nsslapd-pagesizelimit'):
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
else:
with pytest.raises(expected_err):
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
all_results.extend(rdata)
pages += 1
pctrls = [
@@ -363,28 +398,24 @@ def test_search_limits_fail(topology_st, test_user, page_size, users_num,
if pctrls[0].cookie:
# Copy cookie from response control to request control
req_ctrl.cookie = pctrls[0].cookie
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
else:
break # No more pages available
else:
break
finally:
- log.info('Set Directory Manager bind back (test_search_limits_fail)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
change_conf_attr(topology_st, suffix, attr_name, attr_value_bck)
-def test_search_sort_success(topology_st, test_user):
+def test_search_sort_success(topology_st, create_user):
"""Verify that search with a simple paged results control
and a server side sort control returns all entries
it should without errors.
:id: 17d8b150-ed43-41e1-b80f-ee9b4ce45155
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -403,8 +434,7 @@ def test_search_sort_success(topology_st, test_user):
searchreq_attrlist = ['dn', 'sn']
try:
- log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
sort_ctrl = SSSRequestControl(True, ['sn'])
@@ -412,25 +442,25 @@ def test_search_sort_success(topology_st, test_user):
log.info('Initiate ldapsearch with created control instance')
log.info('Collect data with sorting')
controls = [req_ctrl, sort_ctrl]
- results_sorted = paged_search(topology_st, DEFAULT_SUFFIX, controls,
+ results_sorted = paged_search(conn, DEFAULT_SUFFIX, controls,
search_flt, searchreq_attrlist)
log.info('Substring numbers from user DNs')
- r_nums = map(lambda x: int(x[0][8:13]), results_sorted)
+ # r_nums = map(lambda x: int(x[0][8:13]), results_sorted)
+ r_nums = [int(x[0][8:13]) for x in results_sorted]
log.info('Assert that list is sorted')
assert all(r_nums[i] <= r_nums[i + 1] for i in range(len(r_nums) - 1))
finally:
- log.info('Set Directory Manager bind back (test_search_sort_success)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
-def test_search_abandon(topology_st, test_user):
+def test_search_abandon(topology_st, create_user):
"""Verify that search with simple paged results control
can be abandon
:id: 0008538b-7585-4356-839f-268828066978
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -452,36 +482,32 @@ def test_search_abandon(topology_st, test_user):
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
log.info('Initiate a search with a paged results control')
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
log.info('Abandon the search')
- topology_st.standalone.abandon(msgid)
+ conn.abandon(msgid)
log.info('Expect an ldap.TIMEOUT exception, while trying to get the search results')
with pytest.raises(ldap.TIMEOUT):
- topology_st.standalone.result3(msgid, timeout=5)
+ conn.result3(msgid, timeout=5)
finally:
- log.info('Set Directory Manager bind back (test_search_abandon)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
-def test_search_with_timelimit(topology_st, test_user):
+def test_search_with_timelimit(topology_st, create_user):
"""Verify that after performing multiple simple paged searches
to completion, each with a timelimit, it wouldn't fail, if we sleep
for a time more than the timelimit.
:id: 6cd7234b-136c-419f-bf3e-43aa73592cff
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -506,7 +532,7 @@ def test_search_with_timelimit(topology_st, test_user):
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
@@ -514,18 +540,14 @@ def test_search_with_timelimit(topology_st, test_user):
for ii in range(3):
log.info('Iteration %d' % ii)
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls,
- timeout=timelimit)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, search_flt,
+ searchreq_attrlist, serverctrls=controls, timeout=timelimit)
pages = 0
pctrls = []
while True:
log.info('Getting page %d' % (pages,))
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
pages += 1
pctrls = [
c
@@ -537,12 +559,8 @@ def test_search_with_timelimit(topology_st, test_user):
if pctrls[0].cookie:
# Copy cookie from response control to request control
req_ctrl.cookie = pctrls[0].cookie
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls,
- timeout=timelimit)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, search_flt,
+ searchreq_attrlist, serverctrls=controls, timeout=timelimit)
else:
log.info('Done with this search - sleeping %d seconds' % (
timelimit * 2))
@@ -551,24 +569,21 @@ def test_search_with_timelimit(topology_st, test_user):
else:
break
finally:
- log.info('Set Directory Manager bind back (test_search_with_timelimit)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
-@pytest.mark.parametrize('aci_subject',
- ('dns = "localhost.localdomain"',
- 'ip = "::1" or ip = "127.0.0.1"'))
-def test_search_dns_ip_aci(topology_st, test_user, aci_subject):
+def test_search_ip_aci(topology_st, create_user):
"""Verify that after performing multiple simple paged searches
to completion on the suffix with DNS or IP based ACI
:id: bbfddc46-a8c8-49ae-8c90-7265d05b22a9
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
1. Back up and remove all previous ACI from suffix
- 2. Add an anonymous ACI for DNS check
+ 2. Add an anonymous ACI for IP check
3. Bind as test user
4. Search through added users with a simple paged control
5. Perform steps 4 three times in a row
@@ -584,32 +599,31 @@ def test_search_dns_ip_aci(topology_st, test_user, aci_subject):
6. ACI should be successfully returned
7. Results should be the same with ACI with IP subject dn
"""
-
- users_num = 100
+ users_num = 20
page_size = 5
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
+ log.info("test_search_dns_ip_aci: HOSTNAME: " + HOSTNAME)
+ log.info("test_search_dns_ip_aci: IP_ADDRESS: " + IP_ADDRESS)
+
try:
log.info('Back up current suffix ACI')
acis_bck = topology_st.standalone.aci.list(DEFAULT_SUFFIX, ldap.SCOPE_BASE)
log.info('Add test ACI')
+ bind_rule = 'ip = "{}" or ip = "::1" or ip = "{}"'.format(IP_ADDRESS, OLD_IP_ADDRESS)
ACI_TARGET = '(targetattr != "userPassword")'
ACI_ALLOW = '(version 3.0;acl "Anonymous access within domain"; allow (read,compare,search)'
- ACI_SUBJECT = '(userdn = "ldap:///anyone") and (%s);)' % aci_subject
- ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
- try:
- topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_REPLACE,
- 'aci',
- ACI_BODY)])
- except ldap.LDAPError as e:
- log.fatal('Failed to add ACI: error (%s)' % (e.message['desc']))
- raise e
+ ACI_SUBJECT = '(userdn = "ldap:///anyone") and (%s);)' % bind_rule
+ ACI_BODY = ensure_bytes(ACI_TARGET + ACI_ALLOW + ACI_SUBJECT)
+ topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_REPLACE, 'aci', ACI_BODY)])
+ time.sleep(.5)
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ myuri = 'ldap://%s:%d' % (HOSTNAME, topology_st.standalone.port)
+ conn = create_user.bind(TEST_USER_PWD, uri=myuri)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
@@ -618,31 +632,27 @@ def test_search_dns_ip_aci(topology_st, test_user, aci_subject):
log.info('Initiate three searches with a paged results control')
for ii in range(3):
log.info('%d search' % (ii + 1))
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls,
search_flt, searchreq_attrlist)
log.info('%d results' % len(all_results))
assert len(all_results) == len(users_list)
log.info('If we are here, then no error has happened. We are good.')
finally:
- log.info('Set Directory Manager bind back (test_search_dns_ip_aci)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
log.info('Restore ACI')
- topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE,
- 'aci',
- None)])
+ topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)])
for aci in acis_bck:
- topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD,
- 'aci',
- aci.getRawAci())])
- del_users(topology_st, users_list)
+ topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', aci.getRawAci())])
+ time.sleep(1)
+ del_users(users_list)
-def test_search_multiple_paging(topology_st, test_user):
+def test_search_multiple_paging(topology_st, create_user):
"""Verify that after performing multiple simple paged searches
on a single connection without a complition, it wouldn't fail.
:id: 628b29a6-2d47-4116-a88d-00b87405ef7f
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -657,15 +667,15 @@ def test_search_multiple_paging(topology_st, test_user):
4. No error happens
"""
- users_num = 100
- page_size = 30
+ users_num = 20
+ page_size = 5
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
@@ -673,12 +683,9 @@ def test_search_multiple_paging(topology_st, test_user):
for ii in range(3):
log.info('Iteration %d' % ii)
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
pctrls = [
c
for c in rctrls
@@ -687,24 +694,21 @@ def test_search_multiple_paging(topology_st, test_user):
# Copy cookie from response control to request control
req_ctrl.cookie = pctrls[0].cookie
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
finally:
- log.info('Set Directory Manager bind back (test_search_multiple_paging)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
@pytest.mark.parametrize("invalid_cookie", [1000, -1])
-def test_search_invalid_cookie(topology_st, test_user, invalid_cookie):
+def test_search_invalid_cookie(topology_st, create_user, invalid_cookie):
"""Verify that using invalid cookie while performing
search with the simple paged results control throws
a TypeError exception
:id: 107be12d-4fe4-47fe-ae86-f3e340a56f42
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -719,47 +723,40 @@ def test_search_invalid_cookie(topology_st, test_user, invalid_cookie):
4. It should throw a TypeError exception
"""
- users_num = 100
- page_size = 50
+ users_num = 20
+ page_size = 5
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
log.info('Put an invalid cookie (%d) to the control. TypeError is expected' %
invalid_cookie)
req_ctrl.cookie = invalid_cookie
with pytest.raises(TypeError):
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
finally:
- log.info('Set Directory Manager bind back (test_search_invalid_cookie)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
-def test_search_abandon_with_zero_size(topology_st, test_user):
+def test_search_abandon_with_zero_size(topology_st, create_user):
"""Verify that search with simple paged results control
can be abandon using page_size = 0
:id: d2fd9a10-84e1-4b69-a8a7-36ca1427c171
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -779,18 +776,15 @@ def test_search_abandon_with_zero_size(topology_st, test_user):
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=controls)
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
pctrls = [
c
for c in rctrls
@@ -798,17 +792,16 @@ def test_search_abandon_with_zero_size(topology_st, test_user):
]
assert not pctrls[0].cookie
finally:
- log.info('Set Directory Manager bind back (test_search_abandon_with_zero_size)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
-def test_search_pagedsizelimit_success(topology_st, test_user):
+def test_search_pagedsizelimit_success(topology_st, create_user):
"""Verify that search with a simple paged results control
returns all entries it should without errors while
valid value set to nsslapd-pagedsizelimit.
:id: 88193f10-f6f0-42f5-ae9c-ff34b8f9ee8c
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
10 users for the search base
:steps:
@@ -826,42 +819,39 @@ def test_search_pagedsizelimit_success(topology_st, test_user):
page_size = 10
attr_name = 'nsslapd-pagedsizelimit'
attr_value = '20'
- attr_value_bck = change_conf_attr(topology_st, DN_CONFIG,
- attr_name, attr_value)
+ attr_value_bck = change_conf_attr(topology_st, DN_CONFIG, attr_name, attr_value)
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
log.info('%d results' % len(all_results))
assert len(all_results) == len(users_list)
finally:
- log.info('Set Directory Manager bind back (test_search_pagedsizelimit_success)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-pagedsizelimit', attr_value_bck)
+ del_users(users_list)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-pagedsizelimit', attr_value_bck)
@pytest.mark.parametrize('conf_attr,user_attr,expected_rs',
(('5', '15', 'PASS'), ('15', '5', ldap.SIZELIMIT_EXCEEDED)))
-def test_search_nspagedsizelimit(topology_st, test_user,
+def test_search_nspagedsizelimit(topology_st, create_user,
conf_attr, user_attr, expected_rs):
"""Verify that nsPagedSizeLimit attribute overrides
nsslapd-pagedsizelimit while performing search with
the simple paged results control.
:id: b08c6ad2-ba28-447a-9f04-5377c3661d0d
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
10 users for the search base
:steps:
@@ -895,14 +885,12 @@ def test_search_nspagedsizelimit(topology_st, test_user,
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
- conf_attr_bck = change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-pagedsizelimit', conf_attr)
- user_attr_bck = change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedSizeLimit', user_attr)
+ conf_attr_bck = change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology_st, create_user.dn, 'nsPagedSizeLimit', user_attr)
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
@@ -910,34 +898,30 @@ def test_search_nspagedsizelimit(topology_st, test_user,
if expected_rs == ldap.SIZELIMIT_EXCEEDED:
log.info('Expect to fail with SIZELIMIT_EXCEEDED')
with pytest.raises(expected_rs):
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
elif expected_rs == 'PASS':
log.info('Expect to pass')
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
log.info('%d results' % len(all_results))
assert len(all_results) == len(users_list)
finally:
- log.info('Set Directory Manager bind back (test_search_nspagedsizelimit)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-pagedsizelimit', conf_attr_bck)
- change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedSizeLimit', user_attr_bck)
+ del_users(users_list)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology_st, create_user.dn, 'nsPagedSizeLimit', user_attr_bck)
@pytest.mark.parametrize('conf_attr_values,expected_rs',
((('5000', '100', '100'), ldap.ADMINLIMIT_EXCEEDED),
(('5000', '120', '122'), 'PASS')))
-def test_search_paged_limits(topology_st, test_user, conf_attr_values, expected_rs):
+def test_search_paged_limits(topology_st, create_user, conf_attr_values, expected_rs):
"""Verify that nsslapd-idlistscanlimit and
nsslapd-lookthroughlimit can limit the administrator
search abilities.
:id: e0f8b916-7276-4bd3-9e73-8696a4468811
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
10 users for the search base
:steps:
@@ -972,18 +956,14 @@ def test_search_paged_limits(topology_st, test_user, conf_attr_values, expected_
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
- size_attr_bck = change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-sizelimit', conf_attr_values[0])
- pagedsize_attr_bck = change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-pagedsizelimit', conf_attr_values[0])
- idlistscan_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-idlistscanlimit', conf_attr_values[1])
- lookthrough_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-lookthroughlimit', conf_attr_values[2])
+ size_attr_bck = change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-sizelimit', conf_attr_values[0])
+ pagedsize_attr_bck = change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-pagedsizelimit', conf_attr_values[0])
+ idlistscan_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', conf_attr_values[1])
+ lookthrough_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', conf_attr_values[2])
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
@@ -991,37 +971,31 @@ def test_search_paged_limits(topology_st, test_user, conf_attr_values, expected_
if expected_rs == ldap.ADMINLIMIT_EXCEEDED:
log.info('Expect to fail with ADMINLIMIT_EXCEEDED')
with pytest.raises(expected_rs):
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
elif expected_rs == 'PASS':
log.info('Expect to pass')
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
log.info('%d results' % len(all_results))
assert len(all_results) == len(users_list)
finally:
- log.info('Set Directory Manager bind back (test_search_paged_limits)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-sizelimit', size_attr_bck)
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-pagedsizelimit', pagedsize_attr_bck)
- change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
- change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
+ del_users(users_list)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-sizelimit', size_attr_bck)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-pagedsizelimit', pagedsize_attr_bck)
+ change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
+ change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
@pytest.mark.parametrize('conf_attr_values,expected_rs',
((('1000', '100', '100'), ldap.ADMINLIMIT_EXCEEDED),
(('1000', '120', '122'), 'PASS')))
-def test_search_paged_user_limits(topology_st, test_user, conf_attr_values, expected_rs):
+def test_search_paged_user_limits(topology_st, create_user, conf_attr_values, expected_rs):
"""Verify that nsPagedIDListScanLimit and nsPagedLookthroughLimit
override nsslapd-idlistscanlimit and nsslapd-lookthroughlimit
while performing search with the simple paged results control.
:id: 69e393e9-1ab8-4f4e-b4a1-06ca63dc7b1b
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
10 users for the search base
:steps:
@@ -1057,18 +1031,14 @@ def test_search_paged_user_limits(topology_st, test_user, conf_attr_values, expe
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
- lookthrough_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-lookthroughlimit', conf_attr_values[0])
- idlistscan_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-idlistscanlimit', conf_attr_values[0])
- user_idlistscan_attr_bck = change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedIDListScanLimit', conf_attr_values[1])
- user_lookthrough_attr_bck = change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedLookthroughLimit', conf_attr_values[2])
+ lookthrough_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', conf_attr_values[0])
+ idlistscan_attr_bck = change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', conf_attr_values[0])
+ user_idlistscan_attr_bck = change_conf_attr(topology_st, create_user.dn, 'nsPagedIDListScanLimit', conf_attr_values[1])
+ user_lookthrough_attr_bck = change_conf_attr(topology_st, create_user.dn, 'nsPagedLookthroughLimit', conf_attr_values[2])
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
controls = [req_ctrl]
@@ -1076,34 +1046,27 @@ def test_search_paged_user_limits(topology_st, test_user, conf_attr_values, expe
if expected_rs == ldap.ADMINLIMIT_EXCEEDED:
log.info('Expect to fail with ADMINLIMIT_EXCEEDED')
with pytest.raises(expected_rs):
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
elif expected_rs == 'PASS':
log.info('Expect to pass')
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, controls,
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, controls, search_flt, searchreq_attrlist)
log.info('%d results' % len(all_results))
assert len(all_results) == len(users_list)
finally:
- log.info('Set Directory Manager bind back (test_search_paged_user_limits)')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
- change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
- change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM,
- 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
- change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedIDListScanLimit', user_idlistscan_attr_bck)
- change_conf_attr(topology_st, TEST_USER_DN,
- 'nsPagedLookthroughLimit', user_lookthrough_attr_bck)
-
-
-def test_ger_basic(topology_st, test_user):
+ del_users(users_list)
+ change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
+ change_conf_attr(topology_st, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
+ change_conf_attr(topology_st, create_user.dn, 'nsPagedIDListScanLimit', user_idlistscan_attr_bck)
+ change_conf_attr(topology_st, create_user.dn, 'nsPagedLookthroughLimit', user_lookthrough_attr_bck)
+
+
+def test_ger_basic(topology_st, create_user):
"""Verify that search with a simple paged results control
and get effective rights control returns all entries
it should without errors.
:id: 7b0bdfc7-a2f2-4c1a-bcab-f1eb8b330d45
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
varying number of users for the search base
:steps:
@@ -1120,13 +1083,10 @@ def test_ger_basic(topology_st, test_user):
page_size = 4
try:
- log.info('Set bind to directory manager')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
-
spr_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
- ger_ctrl = GetEffectiveRightsControl(True, "dn: " + DN_DM)
+ ger_ctrl = GetEffectiveRightsControl(True, ensure_bytes("dn: " + DN_DM))
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, [spr_ctrl, ger_ctrl],
+ all_results = paged_search(topology_st.standalone, DEFAULT_SUFFIX, [spr_ctrl, ger_ctrl],
search_flt, searchreq_attrlist)
log.info('{} results'.format(len(all_results)))
@@ -1135,14 +1095,15 @@ def test_ger_basic(topology_st, test_user):
assert all(attrs['attributeLevelRights'][0] for dn, attrs in all_results)
finally:
log.info('Remove added users')
- del_users(topology_st, users_list)
+ del_users(users_list)
-def test_multi_suffix_search(topology_st, test_user, new_suffixes):
+def test_multi_suffix_search(topology_st, create_user, new_suffixes):
"""Verify that page result search returns empty cookie
if there is no returned entry.
:id: 9712345b-9e38-4df6-8794-05f12c457d39
+ :customerscenario: True
:setup: Standalone instance, test user for binding,
two suffixes with backends, one is inserted into another,
10 users for the search base within each suffix
@@ -1168,17 +1129,13 @@ def test_multi_suffix_search(topology_st, test_user, new_suffixes):
log.info('Clear the access log')
topology_st.standalone.deleteAccessLogs()
- users_list_1 = add_users(topology_st, users_num / 2, NEW_SUFFIX_1)
- users_list_2 = add_users(topology_st, users_num / 2, NEW_SUFFIX_2)
+ users_list_1 = add_users(topology_st, 10, NEW_SUFFIX_1)
+ users_list_2 = add_users(topology_st, 10, NEW_SUFFIX_2)
try:
- log.info('Set DM bind')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
-
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
- all_results = paged_search(topology_st, NEW_SUFFIX_1, [req_ctrl],
- search_flt, searchreq_attrlist)
+ all_results = paged_search(topology_st.standalone, NEW_SUFFIX_1, [req_ctrl], search_flt, searchreq_attrlist)
log.info('{} results'.format(len(all_results)))
assert len(all_results) == users_num
@@ -1195,15 +1152,17 @@ def test_multi_suffix_search(topology_st, test_user, new_suffixes):
assert pr_cookie_list[-1] == -1
finally:
log.info('Remove added users')
- del_users(topology_st, users_list_1)
- del_users(topology_st, users_list_2)
+ del_users(users_list_1)
+ del_users(users_list_2)
@pytest.mark.parametrize('conf_attr_value', (None, '-1', '1000'))
-def test_maxsimplepaged_per_conn_success(topology_st, test_user, conf_attr_value):
+def test_maxsimplepaged_per_conn_success(topology_st, create_user, conf_attr_value):
"""Verify that nsslapd-maxsimplepaged-per-conn acts according design
:id: 192e2f25-04ee-4ff9-9340-d875dcbe8011
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
20 users for the search base
:steps:
@@ -1223,35 +1182,32 @@ def test_maxsimplepaged_per_conn_success(topology_st, test_user, conf_attr_value
searchreq_attrlist = ['dn', 'sn']
page_size = 4
if conf_attr_value:
- max_per_con_bck = change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-maxsimplepaged-per-conn',
- conf_attr_value)
+ max_per_con_bck = change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-maxsimplepaged-per-conn', conf_attr_value)
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
- all_results = paged_search(topology_st, DEFAULT_SUFFIX, [req_ctrl],
- search_flt, searchreq_attrlist)
+ all_results = paged_search(conn, DEFAULT_SUFFIX, [req_ctrl], search_flt, searchreq_attrlist)
log.info('{} results'.format(len(all_results)))
assert len(all_results) == len(users_list)
finally:
log.info('Remove added users')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
+ del_users(users_list)
if conf_attr_value:
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-maxsimplepaged-per-conn', max_per_con_bck)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-maxsimplepaged-per-conn', max_per_con_bck)
@pytest.mark.parametrize('conf_attr_value', ('0', '1'))
-def test_maxsimplepaged_per_conn_failure(topology_st, test_user, conf_attr_value):
+def test_maxsimplepaged_per_conn_failure(topology_st, create_user, conf_attr_value):
"""Verify that nsslapd-maxsimplepaged-per-conn acts according design
:id: eb609e63-2829-4331-8439-a35f99694efa
+ :customerscenario: True
+ :parametrized: yes
:setup: Standalone instance, test user for binding,
20 users for the search base
:steps:
@@ -1272,40 +1228,62 @@ def test_maxsimplepaged_per_conn_failure(topology_st, test_user, conf_attr_value
search_flt = r'(uid=test*)'
searchreq_attrlist = ['dn', 'sn']
page_size = 4
- max_per_con_bck = change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-maxsimplepaged-per-conn',
- conf_attr_value)
+ max_per_con_bck = change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-maxsimplepaged-per-conn', conf_attr_value)
try:
log.info('Set user bind')
- topology_st.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+ conn = create_user.bind(TEST_USER_PWD)
log.info('Create simple paged results control instance')
req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
with pytest.raises(ldap.UNWILLING_TO_PERFORM):
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=[req_ctrl])
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=[req_ctrl])
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
# If nsslapd-maxsimplepaged-per-conn = 1,
# it should pass this point, but failed on the next search
assert conf_attr_value == '1'
- msgid = topology_st.standalone.search_ext(DEFAULT_SUFFIX,
- ldap.SCOPE_SUBTREE,
- search_flt,
- searchreq_attrlist,
- serverctrls=[req_ctrl])
- rtype, rdata, rmsgid, rctrls = topology_st.standalone.result3(msgid)
+ msgid = conn.search_ext(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE,
+ search_flt, searchreq_attrlist, serverctrls=[req_ctrl])
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
finally:
log.info('Remove added users')
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
- del_users(topology_st, users_list)
- change_conf_attr(topology_st, DN_CONFIG,
- 'nsslapd-maxsimplepaged-per-conn', max_per_con_bck)
+ del_users(users_list)
+ change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-maxsimplepaged-per-conn', max_per_con_bck)
+
+
+def test_search_stress_abandon(create_40k_users, create_user):
+ """Verify that search with a simple paged results control
+ returns all entries it should without errors.
+
+ :id: e154b24a-83d6-11ee-90d1-482ae39447e5
+ :customerscenario: True
+ :feature: Simple paged results
+ :setup: Standalone instance, test user for binding,
+ 40K users in a second backend
+ :steps:
+ 1. Bind as test user
+ 2. Loops a number of times doing:
+ - search through added users with a simple paged control
+ - randomly abandoning the search after a few ms.
+ :expectedresults:
+ 1. Bind should be successful
+ 2. The loop should complete successfully.
+ """
+
+ abandon_rate = 10
+ page_size = 500
+ nbloops = 1000
+ search_flt = r'(uid=*)'
+ searchreq_attrlist = ['dn', 'sn']
+ log.info('Set user bind %s ' % create_user)
+ conn = create_user.bind(TEST_USER_PWD)
+ for idx in range(nbloops):
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ # If the issue #5984 is not fixed the server crashs and the paged search fails with ldap.SERVER_DOWN exception
+ paged_search(conn, create_40k_users.suffix, [req_ctrl], search_flt, searchreq_attrlist, abandon_rate=abandon_rate)
if __name__ == '__main__':
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
index 3f7bef018..0d16b60e6 100644
--- a/ldap/servers/slapd/abandon.c
+++ b/ldap/servers/slapd/abandon.c
@@ -38,6 +38,12 @@ do_abandon(Slapi_PBlock *pb)
Connection *pb_conn = NULL;
Operation *pb_op = NULL;
Operation *o;
+ /* Keep a copy of some data because o may vanish once conn is unlocked */
+ struct {
+ struct timespec hr_time_end;
+ int nentries;
+ int opid;
+ } o_copy;
slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);
slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
@@ -90,8 +96,12 @@ do_abandon(Slapi_PBlock *pb)
PR_EnterMonitor(pb_conn->c_mutex);
for (o = pb_conn->c_ops; o != NULL; o = o->o_next) {
- if (o->o_msgid == id && o != pb_op)
+ if (o->o_msgid == id && o != pb_op) {
+ slapi_operation_time_elapsed(o, &o_copy.hr_time_end);
+ o_copy.nentries = o->o_results.r.r_search.nentries;
+ o_copy.opid = o->o_opid;
break;
+ }
}
if (o != NULL) {
@@ -130,7 +140,8 @@ do_abandon(Slapi_PBlock *pb)
slapi_log_err(SLAPI_LOG_TRACE, "do_abandon", "op not found\n");
}
- if (0 == pagedresults_free_one_msgid_nolock(pb_conn, id)) {
+ PR_ExitMonitor(pb_conn->c_mutex);
+ if (0 == pagedresults_free_one_msgid(pb_conn, id, pageresult_lock_get_addr(pb_conn))) {
slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64
" op=%d ABANDON targetop=Simple Paged Results msgid=%d\n",
pb_conn->c_connid, pb_op->o_opid, id);
@@ -143,15 +154,11 @@ do_abandon(Slapi_PBlock *pb)
" targetop=SUPPRESSED-BY-PLUGIN msgid=%d\n",
pb_conn->c_connid, pb_op->o_opid, id);
} else {
- struct timespec o_hr_time_end;
- slapi_operation_time_elapsed(o, &o_hr_time_end);
slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64 " op=%d ABANDON"
" targetop=%d msgid=%d nentries=%d etime=%" PRId64 ".%010" PRId64 "\n",
- pb_conn->c_connid, pb_op->o_opid, o->o_opid, id,
- o->o_results.r.r_search.nentries, (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
+ pb_conn->c_connid, pb_op->o_opid, o_copy.opid, id,
+ o_copy.nentries, (int64_t)o_copy.hr_time_end.tv_sec, (int64_t)o_copy.hr_time_end.tv_nsec);
}
-
- PR_ExitMonitor(pb_conn->c_mutex);
/*
* Wake up the persistent searches, so they
* can notice if they've been abandoned.
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
index ec316dfb9..80c670d7b 100644
--- a/ldap/servers/slapd/opshared.c
+++ b/ldap/servers/slapd/opshared.c
@@ -889,9 +889,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
next_be = NULL; /* to break the loop */
if (operation->o_status & SLAPI_OP_STATUS_ABANDONED) {
/* It turned out this search was abandoned. */
- pthread_mutex_lock(pagedresults_mutex);
- pagedresults_free_one_msgid_nolock(pb_conn, operation->o_msgid);
- pthread_mutex_unlock(pagedresults_mutex);
+ pagedresults_free_one_msgid(pb_conn, operation->o_msgid, pagedresults_mutex);
/* paged-results-request was abandoned; making an empty cookie. */
pagedresults_set_response_control(pb, 0, estimate, -1, pr_idx);
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
index 8c043f6af..dc9db2c60 100644
--- a/ldap/servers/slapd/pagedresults.c
+++ b/ldap/servers/slapd/pagedresults.c
@@ -34,6 +34,10 @@ pageresult_lock_cleanup()
slapi_ch_free((void**)&lock_hash);
}
+/* Beware to the lock order with c_mutex:
+ * c_mutex is sometime locked while holding pageresult_lock
+ * ==> Do not lock pageresult_lock when holing c_mutex
+ */
pthread_mutex_t *
pageresult_lock_get_addr(Connection *conn)
{
@@ -350,7 +354,7 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
* Used for abandoning - pageresult_lock_get_addr(conn) is already locked in do_abandone.
*/
int
-pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
+pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex)
{
int rc = -1;
int i;
@@ -361,6 +365,7 @@ pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
} else {
slapi_log_err(SLAPI_LOG_TRACE,
"pagedresults_free_one_msgid_nolock", "=> msgid=%d\n", msgid);
+ pthread_mutex_lock(mutex);
for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
PagedResults *prp = conn->c_pagedresults.prl_list + i;
@@ -375,6 +380,7 @@ pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
break;
}
}
+ pthread_mutex_unlock(mutex);
slapi_log_err(SLAPI_LOG_TRACE,
"pagedresults_free_one_msgid_nolock", "<= %d\n", rc);
}
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index b8f736107..a02605774 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -1525,7 +1525,7 @@ int pagedresults_is_timedout_nolock(Connection *conn);
int pagedresults_reset_timedout_nolock(Connection *conn);
int pagedresults_in_use_nolock(Connection *conn);
int pagedresults_free_one(Connection *conn, Operation *op, int index);
-int pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid);
+int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex);
int op_is_pagedresults(Operation *op);
int pagedresults_cleanup_all(Connection *conn, int needlock);
void op_set_pagedresults(Operation *op);
--
2.41.0