dirsrvtests/tests
by Simon Pichugin
dirsrvtests/tests/suites/paged_results/paged_results_test.py | 1017 ++++++++++-
dirsrvtests/tests/suites/paged_results/sss_control.py | 127 +
2 files changed, 1113 insertions(+), 31 deletions(-)
New commits:
commit e00684266db15d3e75405220782496895761e45f
Author: Simon Pichugin <spichugi(a)redhat.com>
Date: Tue Mar 29 11:49:50 2016 +0200
Ticket 48078 - CI test - paged_results - TET part
Description: Add test cases to Simple Paged Results test suite.
These test cases were ported from TET. Also test plans in RST format
were added.
https://fedorahosted.org/389/ticket/48078
Reviewed by: mreynolds, amsharma, vashirov (Thanks!)
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
index 54782bc..6fec5c7 100644
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
@@ -1,28 +1,31 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2016 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
-# See LICENSE for details.
+# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
-import os
-import sys
import time
import ldap
import logging
import pytest
+from random import sample
+from ldap.controls import SimplePagedResultsControl
from lib389 import DirSrv, Entry, tools, tasks
from lib389.tools import DirSrvTools
from lib389._constants import *
from lib389.properties import *
from lib389.tasks import *
from lib389.utils import *
+from sss_control import SSSRequestControl
logging.getLogger(__name__).setLevel(logging.DEBUG)
log = logging.getLogger(__name__)
-installation1_prefix = None
+TEST_USER_NAME = 'simplepaged_test'
+TEST_USER_DN = 'uid=%s,%s' % (TEST_USER_NAME, DEFAULT_SUFFIX)
+TEST_USER_PWD = 'simplepaged_test'
class TopologyStandalone(object):
@@ -33,10 +36,6 @@ class TopologyStandalone(object):
@pytest.fixture(scope="module")
def topology(request):
- global installation1_prefix
- if installation1_prefix:
- args_instance[SER_DEPLOYED_DIR] = installation1_prefix
-
# Creating standalone instance ...
standalone = DirSrv(verbose=False)
args_instance[SER_HOST] = HOST_STANDALONE
@@ -51,43 +50,999 @@ def topology(request):
standalone.create()
standalone.open()
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+ request.addfinalizer(fin)
+
# Clear out the tmp dir
standalone.clearTmpDir(__file__)
return TopologyStandalone(standalone)
-def test_paged_results_init(topology):
- '''
- Write any test suite initialization here(if needed)
- '''
+(a)pytest.fixture(scope="module")
+def test_user(topology):
+ """User for binding operation"""
- return
+ try:
+ topology.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(a)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
-def test_paged_results_(topology):
- '''
- Write a single test here...
- '''
+def add_users(topology, users_num):
+ """Add users to the default suffix
- return
+ Return the list of added user DNs.
+ """
+ users_list = []
+ 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, DEFAULT_SUFFIX)
+ users_list.append(USER_DN)
+ try:
+ topology.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(a)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
+ return users_list
-def test_paged_results_final(topology):
- topology.standalone.delete()
- log.info('paged_results test suite PASSED')
+def del_users(topology, users_list):
+ """Delete users with DNs from given list"""
-def run_isolated():
- global installation1_prefix
- installation1_prefix = None
+ log.info('Deleting %d users' % len(users_list))
+ for user_dn in users_list:
+ try:
+ topology.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
- topo = topology(True)
- test_paged_results_init(topo)
- test_paged_results_(topo)
- test_paged_results_final(topo)
+def change_conf_attr(topology, suffix, attr_name, attr_value):
+ """Change configurational attribute in the given suffix.
-if __name__ == '__main__':
- run_isolated()
+ Returns previous attribute value.
+ """
+
+ try:
+ entries = topology.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.standalone.modify_s(suffix, [(ldap.MOD_DELETE,
+ attr_name,
+ attr_value)])
+ else:
+ topology.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
+
+ return attr_value_bck
+
+
+def paged_search(topology, controls, search_flt, searchreq_attrlist):
+ """Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
+ using Simple Paged Control(should the first item in the
+ list controls.
+ Assert that no cookie left at the end.
+
+ Return the list with results summarized from all pages.
+ """
+
+ pages = 0
+ pctrls = []
+ all_results = []
+ req_ctrl = controls[0]
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ while True:
+ log.info('Getting page %d' % (pages,))
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ all_results.extend(rdata)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ else:
+ break # No more pages available
+ else:
+ break
+
+ assert not pctrls[0].cookie
+ return all_results
+
+
+(a)pytest.mark.parametrize("page_size,users_num",
+ [(6, 5), (5, 5), (5, 25)])
+def test_search_success(topology, test_user, page_size, users_num):
+ """Verify that search with a simple paged results control
+ returns all entries it should without errors.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Search through added users with a simple paged control
+
+ @Assert: All users should be found
+ """
+
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+
+ all_results = paged_search(topology, [req_ctrl],
+ 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+(a)pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err", [
+ (50, 200, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', '100',
+ ldap.UNWILLING_TO_PERFORM),
+ (5, 15, DN_CONFIG, 'nsslapd-timelimit', '20',
+ ldap.UNAVAILABLE_CRITICAL_EXTENSION),
+ (21, 50, DN_CONFIG, 'nsslapd-sizelimit', '20',
+ ldap.SIZELIMIT_EXCEEDED),
+ (21, 50, DN_CONFIG, 'nsslapd-pagedsizelimit', '5',
+ ldap.SIZELIMIT_EXCEEDED),
+ (5, 50, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', '20',
+ ldap.ADMINLIMIT_EXCEEDED)])
+def test_search_limits_fail(topology, test_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.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Set limit attribute to the value that will cause
+ an expected exception
+ 3. Search through added users with a simple paged control
+
+ @Assert: Should fail with appropriate exception
+ """
+
+ users_list = add_users(topology, users_num)
+ attr_value_bck = change_conf_attr(topology, suffix, attr_name, attr_value)
+ conf_param_dict = {attr_name: attr_value}
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ controls = []
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls.append(req_ctrl)
+ if attr_name == 'nsslapd-idlistscanlimit':
+ sort_ctrl = SSSRequestControl(True, ['sn'])
+ controls.append(sort_ctrl)
+ log.info('Initiate ldapsearch with created control instance')
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+
+ time_val = conf_param_dict.get('nsslapd-timelimit')
+ if time_val:
+ time.sleep(int(time_val) + 10)
+
+ pages = 0
+ all_results = []
+ 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.standalone.result3(msgid)
+ else:
+ with pytest.raises(expected_err):
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ all_results.extend(rdata)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ else:
+ break # No more pages available
+ else:
+ break
+ finally:
+ if expected_err == ldap.UNAVAILABLE_CRITICAL_EXTENSION:
+ topology.standalone.open()
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, suffix, attr_name, attr_value_bck)
+
+
+def test_search_sort_success(topology, test_user):
+ """Verify that search with a simple paged results control
+ and a server side sort control returns all entries
+ it should without errors.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Search through added users with a simple paged control
+ and a server side sort control
+
+ @Assert: All users should be found and sorted
+ """
+
+ users_num = 50
+ page_size = 5
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ sort_ctrl = SSSRequestControl(True, ['sn'])
+
+ log.info('Initiate ldapsearch with created control instance')
+ log.info('Collect data with sorting')
+ controls = [req_ctrl, sort_ctrl]
+ results_sorted = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+
+ log.info('Substring numbers from user DNs')
+ r_nums = map(lambda x: int(x[0][8:13]), 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+def test_search_abandon(topology, test_user):
+ """Verify that search with simple paged results control
+ can be abandon
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Search through added users with a simple paged control
+ 3. Abandon the search
+
+ @Assert: It will throw an ldap.TIMEOUT exception, while trying
+ to get the rest of the search results
+ """
+
+ users_num = 10
+ page_size = 2
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, 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.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ log.info('Abandon the search')
+ topology.standalone.abandon(msgid)
+
+ log.info('Expect an ldap.TIMEOUT exception, while trying to get the search results')
+ with pytest.raises(ldap.TIMEOUT):
+ topology.standalone.result3(msgid, timeout=5)
+ finally:
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+def test_search_with_timelimit(topology, test_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.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Search through added users with a simple paged control
+ and timelimit set to 5
+ 3. When the returned cookie is empty, wait 10 seconds
+ 4. Perform steps 2 and 3 three times in a row
+
+ @Assert: No error happens
+ """
+
+ users_num = 100
+ page_size = 50
+ timelimit = 5
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ for ii in range(3):
+ log.info('Iteration %d' % ii)
+ msgid = topology.standalone.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.standalone.result3(msgid)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.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))
+ time.sleep(timelimit * 2)
+ break # No more pages available
+ else:
+ break
+ finally:
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+(a)pytest.mark.parametrize('aci_subject',
+ ('dns = "localhost.localdomain"',
+ 'ip = "::1"'))
+def test_search_dns_ip_aci(topology, test_user, aci_subject):
+ """Verify that after performing multiple simple paged searches
+ to completion on the suffix with DNS or IP based ACI
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated 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
+ 3. Bind as test user
+ 4. Search through added users with a simple paged control
+ 5. Perform steps 4 three times in a row
+ 6. Return ACI to the initial state
+ 7. Go through all steps onece again, but use IP subjectdn
+ insted of DNS
+
+ @Assert: No error happens, all users should be found and sorted
+ """
+
+ users_num = 100
+ page_size = 5
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Back up current suffix ACI')
+ acis_bck = topology.standalone.aci.list(DEFAULT_SUFFIX, ldap.SCOPE_BASE)
+
+ log.info('Add test ACI')
+ 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.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
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, 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 three searches with a paged results control')
+ for ii in range(3):
+ log.info('%d search' % (ii + 1))
+ all_results = paged_search(topology, 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ log.info('Restore ACI')
+ topology.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE,
+ 'aci',
+ None)])
+ for aci in acis_bck:
+ topology.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD,
+ 'aci',
+ aci.getRawAci())])
+ del_users(topology, users_list)
+
+
+def test_search_multiple_paging(topology, test_user):
+ """Verify that after performing multiple simple paged searches
+ on a single connection without a complition, it wouldn't fail.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Initiate the search with a simple paged control
+ 3. Acquire the returned cookie only one time
+ 4. Perform steps 2 and 3 three times in a row
+
+ @Assert: No error happens
+ """
+
+ users_num = 100
+ page_size = 30
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ for ii in range(3):
+ log.info('Iteration %d' % ii)
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ finally:
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+(a)pytest.mark.parametrize("invalid_cookie", [1000, -1])
+def test_search_invalid_cookie(topology, test_user, invalid_cookie):
+ """Verify that using invalid cookie while performing
+ search with the simple paged results control throws
+ a TypeError exception
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Initiate the search with a simple paged control
+ 3. Put an invalid cookie (-1, 1000) to the control
+ 4. Continue the search
+
+ @Assert: It will throw an TypeError exception
+ """
+
+ users_num = 100
+ page_size = 50
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.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.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ finally:
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+def test_search_abandon_with_zero_size(topology, test_user):
+ """Verify that search with simple paged results control
+ can be abandon using page_size = 0
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ variated number of users for the search base
+
+ @Steps:
+ 1. Bind as test user
+ 2. Search through added users with a simple paged control
+ and page_size = 0
+
+ @Assert: No cookie should be returned at all
+ """
+
+ users_num = 10
+ page_size = 0
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+ assert not pctrls[0].cookie
+ finally:
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+
+def test_search_pagedsizelimit_success(topology, test_user):
+ """Verify that search with a simple paged results control
+ returns all entries it should without errors while
+ valid value set to nsslapd-pagedsizelimit.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ 10 users for the search base
+
+ @Steps:
+ 1. Set nsslapd-pagedsizelimit: 20
+ 2. Bind as test user
+ 3. Search through added users with a simple paged control
+ using page_size = 10
+
+ @Assert: All users should be found
+ """
+
+ users_num = 10
+ page_size = 10
+ attr_name = 'nsslapd-pagedsizelimit'
+ attr_value = '20'
+ attr_value_bck = change_conf_attr(topology, DN_CONFIG,
+ attr_name, attr_value)
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ all_results = paged_search(topology, 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', attr_value_bck)
+
+
+(a)pytest.mark.parametrize('conf_attr,user_attr,expected_rs',
+ (('5', '15', 'PASS'), ('15', '5', ldap.SIZELIMIT_EXCEEDED)))
+def test_search_nspagedsizelimit(topology, test_user,
+ conf_attr, user_attr, expected_rs):
+ """Verify that nsPagedSizeLimit attribute overrides
+ nsslapd-pagedsizelimit while performing search with
+ the simple paged results control.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ 10 users for the search base
+
+ @Steps:
+ 1. Set nsslapd-pagedsizelimit: 5
+ 2. Set nsPagedSizeLimit: 15
+ 3. Bind as test user
+ 4. Search through added users with a simple paged control
+ using page_size = 10
+ 5. Bind as Directory Manager
+ 6. Restore all values
+ 7. Set nsslapd-pagedsizelimit: 15
+ 8. Set nsPagedSizeLimit: 5
+ 9. Bind as test user
+ 10. Search through added users with a simple paged control
+ using page_size = 10
+
+ @Assert: After the steps 1-4, it should PASS.
+ After the steps 7-10, it should throw SIZELIMIT_EXCEEDED exception
+ """
+
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ if expected_rs == ldap.SIZELIMIT_EXCEEDED:
+ log.info('Expect to fail with SIZELIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ elif expected_rs == 'PASS':
+ log.info('Expect to pass')
+ all_results = paged_search(topology, 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+
+(a)pytest.mark.parametrize('conf_attr_values,expected_rs',
+ ((('5000', '100', '100'), ldap.ADMINLIMIT_EXCEEDED),
+ (('5000', '120', '122'), 'PASS')))
+def test_search_paged_limits(topology, test_user, conf_attr_values, expected_rs):
+ """Verify that nsslapd-idlistscanlimit and
+ nsslapd-lookthroughlimit can limit the administrator
+ search abilities.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ 10 users for the search base
+
+ @Steps:
+ 1. Set nsslapd-sizelimit and nsslapd-pagedsizelimit to 5000
+ 2. Set nsslapd-idlistscanlimit: 120
+ 3. Set nsslapd-lookthroughlimit: 122
+ 4. Bind as test user
+ 5. Search through added users with a simple paged control
+ using page_size = 10
+ 6. Bind as Directory Manager
+ 7. Set nsslapd-idlistscanlimit: 100
+ 8. Set nsslapd-lookthroughlimit: 100
+ 9. Bind as test user
+ 10. Search through added users with a simple paged control
+ using page_size = 10
+
+ @Assert: After the steps 1-4, it should PASS.
+ After the steps 7-10, it should throw ADMINLIMIT_EXCEEDED exception
+ """
+
+ users_num = 101
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ size_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-sizelimit', conf_attr_values[0])
+ pagedsize_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_values[0])
+ idlistscan_attr_bck = change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-idlistscanlimit', conf_attr_values[1])
+ lookthrough_attr_bck = change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-lookthroughlimit', conf_attr_values[2])
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ if expected_rs == ldap.ADMINLIMIT_EXCEEDED:
+ log.info('Expect to fail with ADMINLIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ elif expected_rs == 'PASS':
+ log.info('Expect to pass')
+ all_results = paged_search(topology, 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-sizelimit', size_attr_bck)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', pagedsize_attr_bck)
+ change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
+ change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
+
+
+(a)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, test_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.
+
+ @Feature: Simple paged results
+
+ @Setup: Standalone instance, test user for binding,
+ 10 users for the search base
+
+ @Steps:
+ 1. Set nsslapd-idlistscanlimit: 1000
+ 2. Set nsslapd-lookthroughlimit: 1000
+ 3. Set nsPagedIDListScanLimit: 120
+ 4. Set nsPagedLookthroughLimit: 122
+ 5. Bind as test user
+ 6. Search through added users with a simple paged control
+ using page_size = 10
+ 7. Bind as Directory Manager
+ 8. Set nsPagedIDListScanLimit: 100
+ 9. Set nsPagedLookthroughLimit: 100
+ 10. Bind as test user
+ 11. Search through added users with a simple paged control
+ using page_size = 10
+
+ @Assert: After the steps 1-4, it should PASS.
+ After the steps 8-11, it should throw ADMINLIMIT_EXCEEDED exception
+ """
+
+ users_num = 101
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ lookthrough_attr_bck = change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-lookthroughlimit', conf_attr_values[0])
+ idlistscan_attr_bck = change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-idlistscanlimit', conf_attr_values[0])
+ user_idlistscan_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedIDListScanLimit', conf_attr_values[1])
+ user_lookthrough_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedLookthroughLimit', conf_attr_values[2])
+
+ try:
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ if expected_rs == ldap.ADMINLIMIT_EXCEEDED:
+ log.info('Expect to fail with ADMINLIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ elif expected_rs == 'PASS':
+ log.info('Expect to pass')
+ all_results = paged_search(topology, 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')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-lookthroughlimit', lookthrough_attr_bck)
+ change_conf_attr(topology, 'cn=config,%s' % DN_LDBM,
+ 'nsslapd-idlistscanlimit', idlistscan_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedIDListScanLimit', user_idlistscan_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedLookthroughLimit', user_lookthrough_attr_bck)
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/dirsrvtests/tests/suites/paged_results/sss_control.py b/dirsrvtests/tests/suites/paged_results/sss_control.py
new file mode 100644
index 0000000..58cd6c5
--- /dev/null
+++ b/dirsrvtests/tests/suites/paged_results/sss_control.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+"""
+ldap.controls.sss - classes for Server Side Sorting
+(see RFC 2891)
+See http://www.python-ldap.org/ for project details.
+$Id: sss.py,v 1.2 2015/10/24 15:52:23 stroeder Exp $
+"""
+
+__all__ = [
+ 'SSSRequestControl',
+ 'SSSResponseControl',
+]
+
+
+import ldap
+from ldap.ldapobject import LDAPObject
+from ldap.controls import (RequestControl, ResponseControl,
+ KNOWN_RESPONSE_CONTROLS, DecodeControlTuples)
+
+from pyasn1.type import univ, namedtype, tag, namedval, constraint
+from pyasn1.codec.ber import encoder, decoder
+
+
+# SortKeyList ::= SEQUENCE OF SEQUENCE {
+# attributeType AttributeDescription,
+# orderingRule [0] MatchingRuleId OPTIONAL,
+# reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+
+class SortKeyType(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('attributeType', univ.OctetString()),
+ namedtype.OptionalNamedType('orderingRule',
+ univ.OctetString().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
+ )
+ ),
+ namedtype.DefaultedNamedType('reverseOrder', univ.Boolean(False).subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))))
+
+
+class SortKeyListType(univ.SequenceOf):
+ componentType = SortKeyType()
+
+
+class SSSRequestControl(RequestControl):
+ '''Order result server side
+ >>> s = SSSRequestControl('-cn')
+ '''
+ controlType = '1.2.840.113556.1.4.473'
+
+ def __init__(
+ self,
+ criticality=False,
+ ordering_rules=None,
+ ):
+ RequestControl.__init__(self,self.controlType,criticality)
+ self.ordering_rules = ordering_rules
+ if isinstance(ordering_rules, basestring):
+ ordering_rules = [ordering_rules]
+ for rule in ordering_rules:
+ rule = rule.split(':')
+ assert len(rule) < 3, 'syntax for ordering rule: [-]<attribute-type>[:ordering-rule]'
+
+ def asn1(self):
+ p = SortKeyListType()
+ for i, rule in enumerate(self.ordering_rules):
+ q = SortKeyType()
+ reverse_order = rule.startswith('-')
+ if reverse_order:
+ rule = rule[1:]
+ if ':' in rule:
+ attribute_type, ordering_rule = rule.split(':')
+ else:
+ attribute_type, ordering_rule = rule, None
+ q.setComponentByName('attributeType', attribute_type)
+ if ordering_rule:
+ q.setComponentByName('orderingRule', ordering_rule)
+ if reverse_order:
+ q.setComponentByName('reverseOrder', 1)
+ p.setComponentByPosition(i, q)
+ return p
+
+ def encodeControlValue(self):
+ return encoder.encode(self.asn1())
+
+
+class SortResultType(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('sortResult', univ.Enumerated().subtype(
+ namedValues=namedval.NamedValues(
+ ('success', 0),
+ ('operationsError', 1),
+ ('timeLimitExceeded', 3),
+ ('strongAuthRequired', 8),
+ ('adminLimitExceeded', 11),
+ ('noSuchAttribute', 16),
+ ('inappropriateMatching', 18),
+ ('insufficientAccessRights', 50),
+ ('busy', 51),
+ ('unwillingToPerform', 53),
+ ('other', 80)),
+ subtypeSpec=univ.Enumerated.subtypeSpec + constraint.SingleValueConstraint(
+ 0, 1, 3, 8, 11, 16, 18, 50, 51, 53, 80))),
+ namedtype.OptionalNamedType('attributeType',
+ univ.OctetString().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
+ )
+ ))
+
+
+class SSSResponseControl(ResponseControl):
+ controlType = '1.2.840.113556.1.4.474'
+
+ def __init__(self,criticality=False):
+ ResponseControl.__init__(self,self.controlType,criticality)
+
+ def decodeControlValue(self, encoded):
+ p, rest = decoder.decode(encoded, asn1Spec=SortResultType())
+ assert not rest, 'all data could not be decoded'
+ self.result = int(p.getComponentByName('sortResult'))
+ self.result_code = p.getComponentByName('sortResult').prettyOut(self.result)
+ self.attribute_type_error = p.getComponentByName('attributeType')
+
+
+KNOWN_RESPONSE_CONTROLS[SSSRequestControl.controlType] = SSSRequestControl
+KNOWN_RESPONSE_CONTROLS[SSSResponseControl.controlType] = SSSResponseControl
7 years, 7 months
ldap/admin
by William Brown
ldap/admin/src/scripts/DSCreate.pm.in | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
New commits:
commit a16da9c70e64b59f145aa5a01fadbfe8430f8265
Author: William Brown <firstyear(a)redhat.com>
Date: Wed May 4 11:49:53 2016 +1000
Ticket 48818 - In docker, no one can hear your process hang.
Bug Description: Docker starts the first process as pid 1. But pid 1 is
special. It's meant to clean up zombies (defunct) processes.
However, our perl code in setup-ds.pl, when we called $startcmd, the
start-dirsrv process was being left defucnt.
Issue is, that because it's defunct, the pid exists, as do the fds. Perl never
returns. Our tests all fail, and setup-ds.pl hangs.
Fix Description: To fix this, we need to implement the process reaping
capability of pid 1 into part of our perl code.
https://fedorahosted.org/389/ticket/48818
Author: wibrown
Review by: nhosoi (Thank you!)
diff --git a/ldap/admin/src/scripts/DSCreate.pm.in b/ldap/admin/src/scripts/DSCreate.pm.in
index 55ecf45..8c3fd04 100644
--- a/ldap/admin/src/scripts/DSCreate.pm.in
+++ b/ldap/admin/src/scripts/DSCreate.pm.in
@@ -34,6 +34,8 @@ use Mozilla::LDAP::Utils qw(normalizeDN);
use Mozilla::LDAP::API qw(ldap_explode_dn);
use Mozilla::LDAP::LDIF;
+use POSIX ":sys_wait_h";
+
use Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(createDSInstance removeDSInstance setDefaults createInstanceScripts
@@ -713,14 +715,20 @@ sub startServer {
$timeout = time + $timeout;
debug(1, "Starting the server: $startcmd\n");
- $? = 0; # clear error condition
- my $output = `$startcmd 2>&1`;
- $code = $?;
- debug(1, "Started the server: code $code\n");
+
+ # We have to do this because docker is incapable of sane process management
+ # Sadly we have to sacrifice output collection, because of perl issues
+ my $cpid = open(my $output, "-|", "$startcmd 2>&1");
+ if ($cpid) {
+ # Parent process
+ waitpid($cpid,0);
+ }
+ close($output);
+ my $code = $?;
if ($code) {
- debug(0, $output);
+ debug(0, "Process returned $code");
} else {
- debug(1, $output);
+ debug(1, "Process returned $code");
}
# try to open the server error log
7 years, 7 months
VERSION.sh
by Noriko Hosoi
VERSION.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
New commits:
commit 83d57eeb8d0c5864ea000b8b834908d8500ae196
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Tue May 3 11:17:22 2016 -0700
bump version to 1.3.5.2
diff --git a/VERSION.sh b/VERSION.sh
index c13c57b..0cede25 100644
--- a/VERSION.sh
+++ b/VERSION.sh
@@ -10,7 +10,7 @@ vendor="389 Project"
# PACKAGE_VERSION is constructed from these
VERSION_MAJOR=1
VERSION_MINOR=3
-VERSION_MAINT=5.1
+VERSION_MAINT=5.2
# NOTE: VERSION_PREREL is automatically set for builds made out of a git tree
VERSION_PREREL=
VERSION_DATE=`date -u +%Y%m%d%H%M%S`
7 years, 7 months
Branch '389-ds-base-1.2.11' - dirsrvtests/tests
by Simon Pichugin
dirsrvtests/tests/tickets/ticket48808_test.py | 337 ++++++++++++++++++++++++++
1 file changed, 337 insertions(+)
New commits:
commit 2550047132de7a94401a987114bbd150e4bcef09
Author: Simon Pichugin <spichugi(a)redhat.com>
Date: Thu Apr 28 11:49:24 2016 +0200
Ticket 48808 - Add test case
Description: Add test case for paged results search returns the blank
list of entries issue.
Bug description: After series of actions, paged result search that
should returns list of entries returns blank list of entries. It is
hardly reproducible manually, but it is easy to reproduce with python
automation.
https://fedorahosted.org/389/ticket/48808
Reviewed by: nhosoi and wbrown (Thanks!)
(cherry picked from commit 91f3e592713ea58602412ed773a497583f2ebd6c)
(cherry picked from commit 99b5048b09e64cea6f8bf5e7d524679960ce0a44)
diff --git a/dirsrvtests/tests/tickets/ticket48808_test.py b/dirsrvtests/tests/tickets/ticket48808_test.py
new file mode 100644
index 0000000..3dbceac
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48808_test.py
@@ -0,0 +1,337 @@
+import time
+import ldap
+import logging
+import pytest
+from random import sample
+from ldap.controls import SimplePagedResultsControl
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+TEST_USER_NAME = 'simplepaged_test'
+TEST_USER_DN = 'uid=%s,%s' % (TEST_USER_NAME, DEFAULT_SUFFIX)
+TEST_USER_PWD = 'simplepaged_test'
+
+
+class TopologyStandalone(object):
+ def __init__(self, standalone):
+ standalone.open()
+ self.standalone = standalone
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ # Creating standalone instance ...
+ standalone = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_STANDALONE
+ args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_standalone = args_instance.copy()
+ standalone.allocate(args_standalone)
+ instance_standalone = standalone.exists()
+ if instance_standalone:
+ standalone.delete()
+ standalone.create()
+ standalone.open()
+
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ standalone.clearTmpDir(__file__)
+
+ return TopologyStandalone(standalone)
+
+
+(a)pytest.fixture(scope="module")
+def test_user(topology):
+ """User for binding operation"""
+
+ try:
+ topology.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(a)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
+
+
+def add_users(topology, users_num):
+ """Add users to the default suffix
+ and return a list of added user DNs.
+ """
+
+ users_list = []
+ 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, DEFAULT_SUFFIX)
+ users_list.append(USER_DN)
+ try:
+ topology.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(a)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
+ return users_list
+
+
+def del_users(topology, 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.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
+
+
+def change_conf_attr(topology, suffix, attr_name, attr_value):
+ """Change configurational attribute in the given suffix.
+ Funtion returns previous attribute value.
+ """
+
+ try:
+ entries = topology.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.standalone.modify_s(suffix, [(ldap.MOD_DELETE,
+ attr_name,
+ attr_value)])
+ else:
+ topology.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
+
+ return attr_value_bck
+
+
+def paged_search(topology, controls, search_flt, searchreq_attrlist):
+ """Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
+ using Simple Paged Control(should the first item in the
+ list controls.
+ Return the list with results summarized from all pages
+ """
+
+ pages = 0
+ pctrls = []
+ all_results = []
+ req_ctrl = controls[0]
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ while True:
+ log.info('Getting page %d' % (pages,))
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ all_results.extend(rdata)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ else:
+ break # no more pages available
+ else:
+ break
+
+ assert not pctrls[0].cookie
+ return all_results
+
+
+def test_ticket48808(topology, test_user):
+ log.info('Run multiple paging controls on a single connection')
+ users_num = 100
+ page_size = 30
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ for ii in xrange(3):
+ log.info('Iteration %d' % ii)
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info('Abandon the search')
+ users_num = 10
+ page_size = 0
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+ assert not pctrls[0].cookie
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info("Search should fail with 'nsPagedSizeLimit = 5'"
+ "and 'nsslapd-pagedsizelimit = 15' with 10 users")
+ conf_attr = '15'
+ user_attr = '5'
+ expected_rs = ldap.SIZELIMIT_EXCEEDED
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Expect to fail with SIZELIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+ log.info("Search should pass with 'nsPagedSizeLimit = 15'"
+ "and 'nsslapd-pagedsizelimit = 5' with 10 users")
+ conf_attr = '5'
+ user_attr = '15'
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Search should PASS')
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ log.info('%d results' % len(all_results))
+ assert len(all_results) == len(users_list)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
7 years, 7 months
Branch '389-ds-base-1.3.4' - dirsrvtests/tests
by Simon Pichugin
dirsrvtests/tests/tickets/ticket48808_test.py | 337 ++++++++++++++++++++++++++
1 file changed, 337 insertions(+)
New commits:
commit 99b5048b09e64cea6f8bf5e7d524679960ce0a44
Author: Simon Pichugin <spichugi(a)redhat.com>
Date: Thu Apr 28 11:49:24 2016 +0200
Ticket 48808 - Add test case
Description: Add test case for paged results search returns the blank
list of entries issue.
Bug description: After series of actions, paged result search that
should returns list of entries returns blank list of entries. It is
hardly reproducible manually, but it is easy to reproduce with python
automation.
https://fedorahosted.org/389/ticket/48808
Reviewed by: nhosoi and wbrown (Thanks!)
(cherry picked from commit 91f3e592713ea58602412ed773a497583f2ebd6c)
diff --git a/dirsrvtests/tests/tickets/ticket48808_test.py b/dirsrvtests/tests/tickets/ticket48808_test.py
new file mode 100644
index 0000000..3dbceac
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48808_test.py
@@ -0,0 +1,337 @@
+import time
+import ldap
+import logging
+import pytest
+from random import sample
+from ldap.controls import SimplePagedResultsControl
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+TEST_USER_NAME = 'simplepaged_test'
+TEST_USER_DN = 'uid=%s,%s' % (TEST_USER_NAME, DEFAULT_SUFFIX)
+TEST_USER_PWD = 'simplepaged_test'
+
+
+class TopologyStandalone(object):
+ def __init__(self, standalone):
+ standalone.open()
+ self.standalone = standalone
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ # Creating standalone instance ...
+ standalone = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_STANDALONE
+ args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_standalone = args_instance.copy()
+ standalone.allocate(args_standalone)
+ instance_standalone = standalone.exists()
+ if instance_standalone:
+ standalone.delete()
+ standalone.create()
+ standalone.open()
+
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ standalone.clearTmpDir(__file__)
+
+ return TopologyStandalone(standalone)
+
+
+(a)pytest.fixture(scope="module")
+def test_user(topology):
+ """User for binding operation"""
+
+ try:
+ topology.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(a)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
+
+
+def add_users(topology, users_num):
+ """Add users to the default suffix
+ and return a list of added user DNs.
+ """
+
+ users_list = []
+ 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, DEFAULT_SUFFIX)
+ users_list.append(USER_DN)
+ try:
+ topology.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(a)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
+ return users_list
+
+
+def del_users(topology, 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.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
+
+
+def change_conf_attr(topology, suffix, attr_name, attr_value):
+ """Change configurational attribute in the given suffix.
+ Funtion returns previous attribute value.
+ """
+
+ try:
+ entries = topology.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.standalone.modify_s(suffix, [(ldap.MOD_DELETE,
+ attr_name,
+ attr_value)])
+ else:
+ topology.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
+
+ return attr_value_bck
+
+
+def paged_search(topology, controls, search_flt, searchreq_attrlist):
+ """Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
+ using Simple Paged Control(should the first item in the
+ list controls.
+ Return the list with results summarized from all pages
+ """
+
+ pages = 0
+ pctrls = []
+ all_results = []
+ req_ctrl = controls[0]
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ while True:
+ log.info('Getting page %d' % (pages,))
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ all_results.extend(rdata)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ else:
+ break # no more pages available
+ else:
+ break
+
+ assert not pctrls[0].cookie
+ return all_results
+
+
+def test_ticket48808(topology, test_user):
+ log.info('Run multiple paging controls on a single connection')
+ users_num = 100
+ page_size = 30
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ for ii in xrange(3):
+ log.info('Iteration %d' % ii)
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info('Abandon the search')
+ users_num = 10
+ page_size = 0
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+ assert not pctrls[0].cookie
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info("Search should fail with 'nsPagedSizeLimit = 5'"
+ "and 'nsslapd-pagedsizelimit = 15' with 10 users")
+ conf_attr = '15'
+ user_attr = '5'
+ expected_rs = ldap.SIZELIMIT_EXCEEDED
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Expect to fail with SIZELIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+ log.info("Search should pass with 'nsPagedSizeLimit = 15'"
+ "and 'nsslapd-pagedsizelimit = 5' with 10 users")
+ conf_attr = '5'
+ user_attr = '15'
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Search should PASS')
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ log.info('%d results' % len(all_results))
+ assert len(all_results) == len(users_list)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
7 years, 7 months
dirsrvtests/tests
by Simon Pichugin
dirsrvtests/tests/tickets/ticket48808_test.py | 337 ++++++++++++++++++++++++++
1 file changed, 337 insertions(+)
New commits:
commit 91f3e592713ea58602412ed773a497583f2ebd6c
Author: Simon Pichugin <spichugi(a)redhat.com>
Date: Thu Apr 28 11:49:24 2016 +0200
Ticket 48808 - Add test case
Description: Add test case for paged results search returns the blank
list of entries issue.
Bug description: After series of actions, paged result search that
should returns list of entries returns blank list of entries. It is
hardly reproducible manually, but it is easy to reproduce with python
automation.
https://fedorahosted.org/389/ticket/48808
Reviewed by: nhosoi and wbrown (Thanks!)
diff --git a/dirsrvtests/tests/tickets/ticket48808_test.py b/dirsrvtests/tests/tickets/ticket48808_test.py
new file mode 100644
index 0000000..3dbceac
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48808_test.py
@@ -0,0 +1,337 @@
+import time
+import ldap
+import logging
+import pytest
+from random import sample
+from ldap.controls import SimplePagedResultsControl
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+TEST_USER_NAME = 'simplepaged_test'
+TEST_USER_DN = 'uid=%s,%s' % (TEST_USER_NAME, DEFAULT_SUFFIX)
+TEST_USER_PWD = 'simplepaged_test'
+
+
+class TopologyStandalone(object):
+ def __init__(self, standalone):
+ standalone.open()
+ self.standalone = standalone
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ # Creating standalone instance ...
+ standalone = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_STANDALONE
+ args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_standalone = args_instance.copy()
+ standalone.allocate(args_standalone)
+ instance_standalone = standalone.exists()
+ if instance_standalone:
+ standalone.delete()
+ standalone.create()
+ standalone.open()
+
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ standalone.clearTmpDir(__file__)
+
+ return TopologyStandalone(standalone)
+
+
+(a)pytest.fixture(scope="module")
+def test_user(topology):
+ """User for binding operation"""
+
+ try:
+ topology.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(a)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
+
+
+def add_users(topology, users_num):
+ """Add users to the default suffix
+ and return a list of added user DNs.
+ """
+
+ users_list = []
+ 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, DEFAULT_SUFFIX)
+ users_list.append(USER_DN)
+ try:
+ topology.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(a)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
+ return users_list
+
+
+def del_users(topology, 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.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
+
+
+def change_conf_attr(topology, suffix, attr_name, attr_value):
+ """Change configurational attribute in the given suffix.
+ Funtion returns previous attribute value.
+ """
+
+ try:
+ entries = topology.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.standalone.modify_s(suffix, [(ldap.MOD_DELETE,
+ attr_name,
+ attr_value)])
+ else:
+ topology.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
+
+ return attr_value_bck
+
+
+def paged_search(topology, controls, search_flt, searchreq_attrlist):
+ """Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
+ using Simple Paged Control(should the first item in the
+ list controls.
+ Return the list with results summarized from all pages
+ """
+
+ pages = 0
+ pctrls = []
+ all_results = []
+ req_ctrl = controls[0]
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ while True:
+ log.info('Getting page %d' % (pages,))
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ all_results.extend(rdata)
+ pages += 1
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ if pctrls:
+ if pctrls[0].cookie:
+ # Copy cookie from response control to request control
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ else:
+ break # no more pages available
+ else:
+ break
+
+ assert not pctrls[0].cookie
+ return all_results
+
+
+def test_ticket48808(topology, test_user):
+ log.info('Run multiple paging controls on a single connection')
+ users_num = 100
+ page_size = 30
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ for ii in xrange(3):
+ log.info('Iteration %d' % ii)
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+
+ req_ctrl.cookie = pctrls[0].cookie
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info('Abandon the search')
+ users_num = 10
+ page_size = 0
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ msgid = topology.standalone.search_ext(DEFAULT_SUFFIX,
+ ldap.SCOPE_SUBTREE,
+ search_flt,
+ searchreq_attrlist,
+ serverctrls=controls)
+ rtype, rdata, rmsgid, rctrls = topology.standalone.result3(msgid)
+ pctrls = [
+ c
+ for c in rctrls
+ if c.controlType == SimplePagedResultsControl.controlType
+ ]
+ assert not pctrls[0].cookie
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+
+ log.info("Search should fail with 'nsPagedSizeLimit = 5'"
+ "and 'nsslapd-pagedsizelimit = 15' with 10 users")
+ conf_attr = '15'
+ user_attr = '5'
+ expected_rs = ldap.SIZELIMIT_EXCEEDED
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Expect to fail with SIZELIMIT_EXCEEDED')
+ with pytest.raises(expected_rs):
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+ log.info("Search should pass with 'nsPagedSizeLimit = 15'"
+ "and 'nsslapd-pagedsizelimit = 5' with 10 users")
+ conf_attr = '5'
+ user_attr = '15'
+ users_num = 10
+ page_size = 10
+ users_list = add_users(topology, users_num)
+ search_flt = r'(uid=test*)'
+ searchreq_attrlist = ['dn', 'sn']
+ conf_attr_bck = change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr)
+ user_attr_bck = change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr)
+
+ log.info('Set user bind')
+ topology.standalone.simple_bind_s(TEST_USER_DN, TEST_USER_PWD)
+
+ log.info('Create simple paged results control instance')
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
+ controls = [req_ctrl]
+
+ log.info('Search should PASS')
+ all_results = paged_search(topology, controls,
+ search_flt, searchreq_attrlist)
+ log.info('%d results' % len(all_results))
+ assert len(all_results) == len(users_list)
+
+ log.info('Set Directory Manager bind back')
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ del_users(topology, users_list)
+ change_conf_attr(topology, DN_CONFIG,
+ 'nsslapd-pagedsizelimit', conf_attr_bck)
+ change_conf_attr(topology, TEST_USER_DN,
+ 'nsPagedSizeLimit', user_attr_bck)
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
7 years, 7 months
configure.ac ldap/admin Makefile.am rpm/389-ds-base.spec.in wrappers/systemd.template.asan.service.in
by William Brown
Makefile.am | 18 ++++++++++++-
configure.ac | 3 +-
ldap/admin/src/scripts/start-dirsrv.in | 25 ++++++++++++------
rpm/389-ds-base.spec.in | 41 +++++++++++++++++++++++++++---
wrappers/systemd.template.asan.service.in | 35 +++++++++++++++++++++++++
5 files changed, 108 insertions(+), 14 deletions(-)
New commits:
commit c9e49213f816ddef19bc0bda8727ddf5576f7368
Author: William Brown <firstyear(a)redhat.com>
Date: Thu Apr 28 14:35:42 2016 +1000
Ticket 48350 - Integrate ASAN into our rpm build process
Bug Description: To improve our QE, we need to be more aggresive during our
tests in QE for detecting issues.
Fix Description: This improves our ASAN integration with our rpms and systemd.
This way, we can give ASAN builds to QE for testing, and we can produce
non-instrumented builds for production releases.
Note that if you run ./configure --enable-asan; make rpms, this will create
an ASAN instrumented RPM.
https://fedorahosted.org/389/ticket/48350
Author: wibrown
Review by: nhosoi (Thanks!)
diff --git a/Makefile.am b/Makefile.am
index 0e9939a..e715fba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,6 +26,12 @@ NUNC_STANS_ON = 0
endif
DS_INCLUDES = -I$(srcdir)/ldap/include -I$(srcdir)/ldap/servers/slapd -I$(srcdir)/include -I. $(NUNC_STANS_INCLUDES)
+if enable_asan
+ASAN_ON = 1
+else
+ASAN_ON = 0
+endif
+
# these paths are dependent on the settings of prefix and exec_prefix which may be specified
# at make time. So we cannot use AC_DEFINE in the configure.ac because that would set the
# values prior to their being defined. Defining them here ensures that they are properly
@@ -1797,6 +1803,7 @@ fixupcmd = sed \
-e 's,@enable_autobind\@,$(enable_autobind),g' \
-e 's,@enable_auto_dn_suffix\@,$(enable_auto_dn_suffix),g' \
-e 's,@enable_presence\@,$(enable_presence),g' \
+ -e 's,@enable_asan\@,$(ASAN_ON),g' \
-e 's,@ECHO_N\@,$(ECHO_N),g' \
-e 's,@ECHO_C\@,$(ECHO_C),g' \
-e 's,@brand\@,$(brand),g' \
@@ -1864,6 +1871,7 @@ fixupcmd = sed \
-e 's,@enable_autobind\@,$(enable_autobind),g' \
-e 's,@enable_auto_dn_suffix\@,$(enable_auto_dn_suffix),g' \
-e 's,@enable_presence\@,$(enable_presence),g' \
+ -e 's,@enable_asan\@,$(ASAN_ON),g' \
-e 's,@ECHO_N\@,$(ECHO_N),g' \
-e 's,@ECHO_C\@,$(ECHO_C),g' \
-e 's,@brand\@,$(brand),g' \
@@ -1925,10 +1933,18 @@ endif
if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
$(fixupcmd) $^ > $@
+
+if enable_asan
+# yes, that is an @ in the filename . . .
+%/$(PACKAGE_NAME)@.service: %/systemd.template.asan.service.in
+ if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
+ $(fixupcmd) $^ > $@
+else
# yes, that is an @ in the filename . . .
%/$(PACKAGE_NAME)@.service: %/systemd.template.service.in
if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
$(fixupcmd) $^ > $@
+endif
%/$(PACKAGE_NAME).systemd: %/systemd.template.sysconfig
if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi
@@ -1974,7 +1990,7 @@ rpmbrprep: dist-bzip2 rpmroot
cp $(distdir).tar.bz2 $(RPMBUILD)/SOURCES
cp $(srcdir)/rpm/389-ds-base-git.sh $(RPMBUILD)/SOURCES
cp $(srcdir)/rpm/389-ds-base-devel.README $(RPMBUILD)/SOURCES
- sed -e "s/__VERSION__/$(RPM_VERSION)/" -e "s/__RELEASE__/$(RPM_RELEASE)/" -e "s/__NUNC_STANS_ON__/$(NUNC_STANS_ON)/" < $(abs_builddir)/rpm/389-ds-base.spec > $(RPMBUILD)/SPECS/389-ds-base.spec
+ sed -e "s/__VERSION__/$(RPM_VERSION)/" -e "s/__RELEASE__/$(RPM_RELEASE)/" -e "s/__NUNC_STANS_ON__/$(NUNC_STANS_ON)/" -e "s/__ASAN_ON__/$(ASAN_ON)/" < $(abs_builddir)/rpm/389-ds-base.spec > $(RPMBUILD)/SPECS/389-ds-base.spec
# Requires rpmdevtools. Consider making this a dependancy of rpms.
rpmsources: rpmbrprep
diff --git a/configure.ac b/configure.ac
index 9544bbc..85e99f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,7 +69,7 @@ AC_MSG_CHECKING(for --enable-debug)
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], [Enable debug features (default: no)]),
[
AC_MSG_RESULT(yes)
- debug_defs="-g3 -DDEBUG -DMCC_DEBUG"
+ debug_defs="-g3 -DDEBUG -DMCC_DEBUG -O0"
],
[
AC_MSG_RESULT(no)
@@ -88,6 +88,7 @@ AC_ARG_ENABLE(asan, AS_HELP_STRING([--enable-asan], [Enable gcc address sanitize
asan_defs=""
])
AC_SUBST([asan_defs])
+AM_CONDITIONAL(enable_asan,test "$enable_asan" = "yes")
AM_CONDITIONAL([RPM_HARDEND_CC], [test -f /usr/lib/rpm/redhat/redhat-hardened-cc1])
AC_MSG_CHECKING(for --enable-gcc-security)
diff --git a/ldap/admin/src/scripts/start-dirsrv.in b/ldap/admin/src/scripts/start-dirsrv.in
index 4bc0ba2..410786b 100755
--- a/ldap/admin/src/scripts/start-dirsrv.in
+++ b/ldap/admin/src/scripts/start-dirsrv.in
@@ -60,6 +60,14 @@ start_instance() {
return 1
fi
else
+ if test 1 -eq @enable_asan@; then
+ echo "NOTICE: Starting instance ${SERV_ID} with ASAN options."
+ echo "This is probably not what you want. Please contact support."
+ : ${ASAN_LOG_PATH:=$RUN_DIR/ns-slapd-${SERV_ID}.asan}
+ echo "Asan errors will go to ${ASAN_LOG_PATH}*"
+ export ASAN_OPTIONS="detect_leaks=1 symbolize=1 detect_deadlocks=1 log_path=${ASAN_LOG_PATH}"
+ export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
+ fi
$SERVERBIN_DIR/ns-slapd -D $CONFIG_DIR -i $PIDFILE "$@"
if [ $? -ne 0 ]; then
return 1
@@ -70,16 +78,17 @@ start_instance() {
max_count=${PID_TIME:-600}
while test $loop_counter -le $max_count; do
loop_counter=`expr $loop_counter + 1`
- if test ! -f $PIDFILE ; then
- if kill -s 0 $PID > /dev/null 2>&1 ; then
- sleep 1
- else
- echo Server failed to start !!! Please check errors log for problems
- return 1
- fi
- else
+ if test -f $PIDFILE ; then
PID=`cat $PIDFILE`
return 0;
+ else
+ # I'm not sure what this meant to achieve, but $PID is 0 here.
+ if kill -s 0 $PID > /dev/null 2>&1 ; then
+ sleep 1
+ else
+ echo Server failed to start !!! Please check errors log for problems
+ return 1
+ fi
fi
done
echo Server not running!! Failed to start ns-slapd process. Please check the errors log for problems.
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index e4665e8..e3f7e1d 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -1,5 +1,8 @@
%global pkgname dirsrv
+
+# This is used in certain builds to help us know if it has extra features.
+%global variant base
# for a pre-release, define the prerel field e.g. .a1 .rc2 - comment out for official release
# also remove the space between % and global - this space is needed because
# fedpkg verrel stupidly ignores comment lines
@@ -25,6 +28,13 @@
%global jemalloc_ver 3.6.0
%endif
+# This enables an ASAN build. This should not go to production, so we rename.
+%global use_asan __ASAN_ON__
+%if 0%{?use_asan:1}
+%global variant base-asan
+%endif
+
+
# fedora 15 and later uses tmpfiles.d
# otherwise, comment this out
%{!?with_tmpfiles_d: %global with_tmpfiles_d %{_sysconfdir}/tmpfiles.d}
@@ -35,7 +45,7 @@
# set PIE flag
%global _hardened_build 1
-Summary: 389 Directory Server (base)
+Summary: 389 Directory Server (%{variant})
Name: 389-ds-base
Version: __VERSION__
Release: __RELEASE__%{?dist}
@@ -79,6 +89,9 @@ BuildRequires: tcp_wrappers
BuildRequires: pam-devel
BuildRequires: systemd-units
BuildRequires: systemd-devel
+%if 0%{?use_asan:1}
+BuildRequires: libasan
+%endif
# this is needed for using semanage from our setup scripts
Requires: policycoreutils-python
@@ -125,6 +138,11 @@ Requires: perl-Socket
Requires: perl-NetAddr-IP
Requires: systemd-libs
+%if 0%{?use_asan:1}
+Requires: libasan
+Requires: llvm
+%endif
+
Source0: http://port389.org/sources/%{name}-%{version}%{?prerel}.tar.bz2
# 389-ds-git.sh should be used to generate the source tarball from git
Source1: %{name}-git.sh
@@ -137,12 +155,21 @@ Source3: http://www.port389.org/binaries/jemalloc-%{jemalloc_ver}.tar.b
Source4: https://git.fedorahosted.org/cgit/nunc-stans.git/snapshot/nunc-stans-%{nu...
%endif
+%if 0%{?use_asan:1}
+%description
+389 Directory Server is an LDAPv3 compliant server. The base package includes
+the LDAP server and command line utilities for server administration.
+WARNING! This build is linked to Address Sanitisation libraries. This probably
+isn't what you want. Please contact support immediately.
+Please see http://seclists.org/oss-sec/2016/q1/363 for more information.
+%else
%description
389 Directory Server is an LDAPv3 compliant server. The base package includes
the LDAP server and command line utilities for server administration.
+%endif
%package libs
-Summary: Core libraries for 389 Directory Server
+Summary: Core libraries for 389 Directory Server (%{variant})
Group: System Environment/Daemons
BuildRequires: nspr-devel
BuildRequires: nss-devel
@@ -179,7 +206,7 @@ are used by the main package and the -devel package. This allows the -devel
package to be installed with just the -libs package and without the main package.
%package devel
-Summary: Development libraries for 389 Directory Server
+Summary: Development libraries for 389 Directory Server (%{variant})
Group: Development/Libraries
Requires: %{name}-libs = %{version}-%{release}
Requires: pkgconfig
@@ -246,14 +273,20 @@ OPENLDAP_FLAG="--with-openldap"
%{?with_tmpfiles_d: TMPFILES_FLAG="--with-tmpfiles-d=%{with_tmpfiles_d}"}
# hack hack hack https://bugzilla.redhat.com/show_bug.cgi?id=833529
NSSARGS="--with-svrcore-inc=%{_includedir} --with-svrcore-lib=%{_libdir} --with-nss-lib=%{_libdir} --with-nss-inc=%{_includedir}/nss3"
+
%if %{use_nunc_stans}
NUNC_STANS_FLAGS="--enable-nunc-stans --with-nunc-stans=../nunc-stans-%{nunc_stans_ver}"
%endif
+
+%if 0%{?use_asan:1}
+ASAN_FLAGS="--enable-asan --enable-debug"
+%endif
+
%configure --enable-autobind --with-selinux $OPENLDAP_FLAG $TMPFILES_FLAG \
--with-systemdsystemunitdir=%{_unitdir} \
--with-systemdsystemconfdir=%{_sysconfdir}/systemd/system \
--with-systemdgroupname=%{groupname} $NSSARGS $NUNC_STANS_FLAGS \
- --with-systemd
+ --with-systemd $ASAN_FLAGS
# Generate symbolic info for debuggers
export XCFLAGS=$RPM_OPT_FLAGS
diff --git a/wrappers/systemd.template.asan.service.in b/wrappers/systemd.template.asan.service.in
new file mode 100644
index 0000000..af91f16
--- /dev/null
+++ b/wrappers/systemd.template.asan.service.in
@@ -0,0 +1,35 @@
+# you usually do not want to edit this file - instead, edit the
+# @initconfigdir@/@package_name@.systemd file instead - otherwise,
+# do not edit this file in /lib/systemd/system - instead, do the following:
+# cp /lib/systemd/system/dirsrv\@.service /etc/systemd/system/dirsrv\@.service
+# mkdir -p /etc/systemd/system/@systemdgroupname@.wants
+# edit /etc/systemd/system/dirsrv\@.service - uncomment the LimitNOFILE=8192 line
+# where %i is the name of the instance
+# you may already have a symlink in
+# /etc/systemd/system/@systemdgroupname@.wants/dirsrv(a)%i.service pointing to
+# /lib/systemd/system/dirsrv\@.service - you will have to change it to link
+# to /etc/systemd/system/dirsrv\@.service instead
+# ln -s /etc/systemd/system/dirsrv\@.service /etc/systemd/system/@systemdgroupname@.wants/dirsrv(a)%i.service
+# systemctl daemon-reload
+# systemctl (re)start @systemdgroupname@
+[Unit]
+Description=@capbrand@ Directory Server with ASAN %i.
+PartOf=@systemdgroupname@
+After=chronyd.service network.service
+
+[Service]
+Type=notify
+EnvironmentFile=@initconfigdir@/@package_name@
+EnvironmentFile=@initconfigdir@/@package_name@-%i
+PIDFile=@localstatedir@/run/@package_name(a)/slapd-%i.pid
+# We can't symbolize here, as llvm symbolize crashes when it goes near systemd.
+Environment='ASAN_OPTIONS="detect_leaks=1 symbolize=0 log_path=@localstatedir@/run/@package_name(a)/ns-slapd-%i.asan detect_deadlocks=1"'
+LimitCORE=infinity
+ExecStart=@sbindir@/ns-slapd -D @instconfigdir@/slapd-%i -i @localstatedir@/run/@package_name(a)/slapd-%i.pid
+# if you need to set other directives e.g. LimitNOFILE=8192
+# set them in this file
+.include @initconfigdir@/@package_name@.systemd
+
+[Install]
+WantedBy=dirsrv.target
+
7 years, 7 months
ldap/admin
by Mark Reynolds
ldap/admin/src/scripts/ns-accountstatus.pl.in | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
New commits:
commit c39bed2e05cca1c73354dcca9f39f3d90594dc9a
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue May 3 15:45:41 2016 -0400
Ticket 48815 - ns-accountstatus.sh does handle DN's with single quotes
Bug Description: When a DN is encountered that has a single quote in it
the script fails because of how the script quotes the DN.
Fix Description: There is no need to quote the DN when using it in a filter.
https://fedorahosted.org/389/ticket/48813
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/admin/src/scripts/ns-accountstatus.pl.in b/ldap/admin/src/scripts/ns-accountstatus.pl.in
index 5d92220..0d52ec9 100644
--- a/ldap/admin/src/scripts/ns-accountstatus.pl.in
+++ b/ldap/admin/src/scripts/ns-accountstatus.pl.in
@@ -643,7 +643,7 @@ sub getSuffix
#
debug("\tSuffix from the entry: #@suffixN#\n");
$info{base} = "cn=mapping tree, cn=config";
- $info{filter} = "cn=\"@suffixN\"";
+ $info{filter} = "cn=@suffix";
$info{scope} = "one";
$info{attrs} = "cn";
@mapping = DSUtil::ldapsrch_ext(%info);
@@ -849,7 +849,7 @@ for(my $i = 0; $i <= $#entries; $i++){
}
#
- # Gather the Account Ppoliy PLugin information(if available)
+ # Gather the Account Policy Plugin information (if available)
#
($acct_policy_enabled, $stateattr, $altstateattr, $limit) = getAcctPolicy(\%info, $entry);
7 years, 7 months
ldap/admin
by Mark Reynolds
ldap/admin/src/scripts/ns-accountstatus.pl.in | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
New commits:
commit 7b7d22cd80cc827ab4c2b801fcfe3f02f1a3e690
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue May 3 15:51:51 2016 -0400
Ticket 48269 - ns-accountstatus status message improvement
Description: With the addition of support of account policy inactivity
limits, the inactivated messages should be more detailed.
https://fedorahosted.org/389/ticket/48269
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/admin/src/scripts/ns-accountstatus.pl.in b/ldap/admin/src/scripts/ns-accountstatus.pl.in
index 139160b..5d92220 100644
--- a/ldap/admin/src/scripts/ns-accountstatus.pl.in
+++ b/ldap/admin/src/scripts/ns-accountstatus.pl.in
@@ -920,10 +920,10 @@ for(my $i = 0; $i <= $#entries; $i++){
if ($verbose){
printVerbose(\%info, "@suffixN", $entry, $createtime,
$modifytime, $lastlogintime,
- "inactivated through $throughRole", $limit,
+ "inactivated (indirectly through role: $throughRole)", $limit,
$acct_policy_enabled);
} else {
- out("$entry - inactivated through $throughRole.\n");
+ out("$entry - inactivated (indirectly through role: $throughRole).\n");
}
if($keep_processing){
next;
@@ -933,10 +933,10 @@ for(my $i = 0; $i <= $#entries; $i++){
debug("$entry locked individually\n");
if ($verbose){
printVerbose(\%info, "@suffixN", $entry, $createtime,
- $modifytime, $lastlogintime, "inactivated", $limit,
+ $modifytime, $lastlogintime, "inactivated (directly locked)", $limit,
$acct_policy_enabled);
} else {
- out("$entry - inactivated.\n");
+ out("$entry - inactivated (directly locked).\n");
}
if($keep_processing){
next;
@@ -1025,10 +1025,10 @@ for(my $i = 0; $i <= $#entries; $i++){
}
if($verbose){
printVerbose(\%info, "@suffixN", $entry, $createtime,
- $modifytime, $lastlogintime, "inactivated", $limit,
+ $modifytime, $lastlogintime, "inactivated (directly locked)", $limit,
$acct_policy_enabled);
} else {
- out("$entry - inactivated.\n");
+ out("$entry - inactivated (directly locked).\n");
}
if($keep_processing){
next;
7 years, 7 months
Branch '389-ds-base-1.2.11' - dirsrvtests/tests ldap/servers
by Mark Reynolds
dirsrvtests/tests/suites/password/pwp_history_test.py | 264 ++++++++++++++++++
ldap/servers/slapd/modify.c | 4
ldap/servers/slapd/proto-slap.h | 1
ldap/servers/slapd/pw.c | 29 +
4 files changed, 298 insertions(+)
New commits:
commit 0a5047043051701d5b361500f605d782a2befa1d
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue May 3 09:57:36 2016 -0400
Ticket 48813 - password history is not updated when an admin resets the password
Bug Description: When an admin resets a password the current password is not
stored in the password history. This incorrectly allows the
user to reuse the previous password after the reset.
Fix Description: When a password is being reset by an "admin", still grab the
old password so we can correctly update the password history.
https://fedorahosted.org/389/ticket/48813
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 9c310b09c481a32b1a012371c688c65156b33472)
(cherry picked from commit b357e443d06f32bcad2f410868299d43153dca62)
diff --git a/dirsrvtests/tests/suites/password/pwp_history_test.py b/dirsrvtests/tests/suites/password/pwp_history_test.py
new file mode 100644
index 0000000..3f66efd
--- /dev/null
+++ b/dirsrvtests/tests/suites/password/pwp_history_test.py
@@ -0,0 +1,264 @@
+import os
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+
+class TopologyStandalone(object):
+ """ Topology class """
+ def __init__(self, standalone):
+ """ init """
+ standalone.open()
+ self.standalone = standalone
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ """
+ Creating standalone instance ...
+ """
+ standalone = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_STANDALONE
+ args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_standalone = args_instance.copy()
+ standalone.allocate(args_standalone)
+ instance_standalone = standalone.exists()
+ if instance_standalone:
+ standalone.delete()
+ standalone.create()
+ standalone.open()
+
+ # Delete each instance in the end
+ def fin():
+ """ Clean up instance """
+ standalone.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ standalone.clearTmpDir(__file__)
+
+ return TopologyStandalone(standalone)
+
+
+def test_pwp_history_test(topology):
+ """
+ Test password policy history feature:
+ - Test password history is enforced
+ - Test password history works after an Admin resets the password
+ - Test that the correct number of passwords are stored in history
+ """
+
+ USER_DN = 'uid=testuser,' + DEFAULT_SUFFIX
+
+ #
+ # Configure password history policy and add a test user
+ #
+ try:
+ topology.standalone.modify_s("cn=config",
+ [(ldap.MOD_REPLACE,
+ 'passwordHistory', 'on'),
+ (ldap.MOD_REPLACE,
+ 'passwordInHistory', '3'),
+ (ldap.MOD_REPLACE,
+ 'passwordChange', 'on'),
+ (ldap.MOD_REPLACE,
+ 'passwordStorageScheme', 'CLEAR')])
+ log.info('Configured password policy.')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to configure password policy: ' + str(e))
+ assert False
+
+ try:
+ topology.standalone.add_s(Entry((USER_DN, {
+ 'objectclass': ['top', 'extensibleObject'],
+ 'sn': 'user',
+ 'cn': 'test user',
+ 'uid': 'testuser',
+ 'userpassword': 'password'})))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add test user' + USER_DN + ': error ' + str(e))
+ assert False
+
+ #
+ # Test that password history is enforced.
+ #
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user: ' + str(e))
+ assert False
+
+ # Attempt to change password to the same password
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password')])
+ log.info('Incorrectly able to to set password to existing password.')
+ assert False
+ except ldap.CONSTRAINT_VIOLATION:
+ log.info('Password change correctly rejected')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to change password: ' + str(e))
+ assert False
+
+ #
+ # Keep changing password until we fill the password history (3)
+ #
+
+ # password1
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password1')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to change password: ' + str(e))
+ assert False
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password1')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user using "password1": ' + str(e))
+ assert False
+
+ # password2
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password2')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to change password: ' + str(e))
+ assert False
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password2')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user using "password2": ' + str(e))
+ assert False
+
+ # password3
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password3')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to change password: ' + str(e))
+ assert False
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password3')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user using "password3": ' + str(e))
+ assert False
+
+ # password4
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password4')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to change password: ' + str(e))
+ assert False
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password4')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user using "password4": ' + str(e))
+ assert False
+
+ #
+ # Check that we only have 3 passwords stored in history\
+ #
+ try:
+ entry = topology.standalone.search_s(USER_DN, ldap.SCOPE_BASE,
+ 'objectclass=*',
+ ['passwordHistory'])
+ pwds = entry[0].getValues('passwordHistory')
+ if len(pwds) != 3:
+ log.fatal('Incorrect number of passwords stored in histry: %d' %
+ len(pwds))
+ assert False
+ else:
+ log.info('Correct number of passwords found in history.')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to get user entry: ' + str(e))
+ assert False
+
+ #
+ # Attempt to change the password to previous passwords
+ #
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password1')])
+ log.info('Incorrectly able to to set password to previous password1.')
+ assert False
+ except ldap.CONSTRAINT_VIOLATION:
+ log.info('Password change correctly rejected')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to change password: ' + str(e))
+ assert False
+
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password2')])
+ log.info('Incorrectly able to to set password to previous password2.')
+ assert False
+ except ldap.CONSTRAINT_VIOLATION:
+ log.info('Password change correctly rejected')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to change password: ' + str(e))
+ assert False
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password3')])
+ log.info('Incorrectly able to to set password to previous password3.')
+ assert False
+ except ldap.CONSTRAINT_VIOLATION:
+ log.info('Password change correctly rejected')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to change password: ' + str(e))
+ assert False
+
+ #
+ # Reset password by Directory Manager(admin reset)
+ #
+ try:
+ topology.standalone.simple_bind_s(DN_DM, PASSWORD)
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as rootDN: ' + str(e))
+ assert False
+
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword',
+ 'password-reset')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to reset password: ' + str(e))
+ assert False
+
+ # Try and change the password to the previous password before the reset
+ try:
+ topology.standalone.simple_bind_s(USER_DN, 'password-reset')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as user: ' + str(e))
+ assert False
+
+ try:
+ topology.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE,
+ 'userpassword', 'password4')])
+ log.info('Incorrectly able to to set password to previous password4.')
+ assert False
+ except ldap.CONSTRAINT_VIOLATION:
+ log.info('Password change correctly rejected')
+ except ldap.LDAPError as e:
+ log.fatal('Failed to attempt to change password: ' + str(e))
+ assert False
+
+ log.info('Test suite PASSED.')
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
index c67ef14..4bcc827 100644
--- a/ldap/servers/slapd/modify.c
+++ b/ldap/servers/slapd/modify.c
@@ -1259,6 +1259,10 @@ static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old
* just return success.
*/
if(pw_is_pwp_admin(pb, pwpolicy)){
+ if (!SLAPI_IS_MOD_DELETE(mod->mod_op) && pwpolicy->pw_history){
+ /* Updating pw history, get the old password */
+ get_old_pw(pb, &sdn, old_pw);
+ }
rc = 1;
goto done;
}
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index decc29e..3b00c80 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -906,6 +906,7 @@ int check_pw_syntax( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals,
char **old_pw, Slapi_Entry *e, int mod_op );
int check_pw_syntax_ext( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals,
char **old_pw, Slapi_Entry *e, int mod_op, Slapi_Mods *smods );
+void get_old_pw( Slapi_PBlock *pb, const Slapi_DN *sdn, char **old_pw);
int check_account_lock( Slapi_PBlock *pb, Slapi_Entry * bind_target_entry, int pwresponse_req, int account_inactivation_only /*no wire/no pw policy*/);
int check_pw_minage( Slapi_PBlock *pb, const Slapi_DN *sdn, struct berval **vals) ;
void add_password_attrs( Slapi_PBlock *pb, Operation *op, Slapi_Entry *e );
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index fe3c025..2785bd7 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -1085,6 +1085,35 @@ retry:
}
/*
+ * Get the old password -used by password admin so we properly
+ * update pw history when reseting a password.
+ */
+void
+get_old_pw( Slapi_PBlock *pb, const Slapi_DN *sdn, char **old_pw )
+{
+ Slapi_Entry *e = NULL;
+ Slapi_Value **va = NULL;
+ Slapi_Attr *attr = NULL;
+ char *dn = (char*)slapi_sdn_get_ndn(sdn);
+
+ e = get_entry ( pb, dn );
+ if ( e == NULL ) {
+ return;
+ }
+
+ /* get current password, and remember it */
+ attr = attrlist_find(e->e_attrs, "userpassword");
+ if ( attr && !valueset_isempty(&attr->a_present_values) ) {
+ va = valueset_get_valuearray(&attr->a_present_values);
+ *old_pw = slapi_ch_strdup(slapi_value_get_string(va[0]));
+ } else {
+ *old_pw = NULL;
+ }
+
+ slapi_entry_free(e);
+}
+
+/*
* Basically, h0 and h1 must be longer than GENERALIZED_TIME_LENGTH.
*/
static int
7 years, 7 months