def test_filter_validation_enabled(topology_st): """Test that queries which are invalid, are correctly rejected by the server. :id: 05afdbbd-0d7f-4774-958c-2139827fed70 :setup: Standalone instance :steps: 1. Search a well formed query 2. Search a poorly formed query 3. Search a poorly formed complex (and/or) query 4. Test the server can be restarted :expectedresults: 1. No warnings 2. Query is rejected (err) 3. Query is rejected (err) 4. Server restarts """ inst = topology_st.standalone # In case the default has changed, we set the value to warn. inst.config.set("nsslapd-verify-filter-schema", "reject-invalid") raw_objects = DSLdapObjects(inst, basedn=DEFAULT_SUFFIX) # Check a good query has no errors. r = raw_objects.filter("(objectClass=*)") with pytest.raises(ldap.UNWILLING_TO_PERFORM): # Check a bad one DOES emit an error. r = raw_objects.filter("(a=a)") with pytest.raises(ldap.UNWILLING_TO_PERFORM): # Check a bad complex one does emit an error. r = raw_objects.filter("(&(a=a)(b=b)(objectClass=*))") # Does restart work? inst.restart()
def test_num_subordinates_with_monitor_suffix(topo): """This test is to compare the numSubordinates value on the root entry with the actual number of direct subordinate(s). :id: fdcfe0ac-33c3-4252-bf38-79819ec58a51 :setup: Single instance :steps: 1. Create sample entries and perform a search with basedn as cn=monitor, filter as "(objectclass=*)" and scope as base. 2. Extract the numSubordinates value. 3. Perform another search with basedn as cn=monitor, filter as "(|(objectclass=*)(objectclass=ldapsubentry))" and scope as one. 4. Compare numSubordinates value with the number of sub-entries. :expectedresults: 1. Success 2. Success 3. Success 4. Should be same """ raw_objects = DSLdapObjects(topo.standalone, basedn='cn=monitor') filter1 = raw_objects.filter("(objectclass=*)", scope=0) num_subordinates_val = filter1[0].get_attr_val_int('numSubordinates') filter2 = raw_objects.filter( "(|(objectclass=*)(objectclass=ldapsubentry))", scope=1) assert len(filter2) == num_subordinates_val
def test_produce_division_by_zero(topology_st, create_dummy_mount, change_config): """Test dirsrv will not crash when division by zero occurs :id: 51b11093-8851-41bd-86cb-217b1a3339c7 :customerscenario: True :setup: Standalone :steps: 1. Turn on disk monitoring 2. Go below the threshold 3. Check DS is up and not entering shutdown mode :expectedresults: 1. Success 2. Success 3. Success """ standalone = topology_st.standalone log.info( 'Check search works before changing the nsslapd-auditlog attribute') try: DSLdapObjects(topology_st.standalone, basedn='cn=disk space,cn=monitor').filter( "(objectclass=*)", scope=0) except ldap.SERVER_DOWN as e: log.info('Test failed - dirsrv crashed') assert False log.info('Change location of nsslapd-auditlog') standalone.config.set( 'nsslapd-auditlog', '/var/log/dirsrv/slapd-{}/tmp/audit'.format(standalone.serverid)) log.info('Check search will not fail') try: DSLdapObjects(topology_st.standalone, basedn='cn=disk space,cn=monitor').filter( "(objectclass=*)", scope=0) except ldap.SERVER_DOWN as e: log.info('Test failed - dirsrv crashed') assert False log.info('If passed, run search again just in case') try: DSLdapObjects(topology_st.standalone, basedn='cn=disk space,cn=monitor').filter( "(objectclass=*)", scope=0) except ldap.SERVER_DOWN as e: log.info('Test failed - dirsrv crashed') assert False
def test_filter_validation_warn_safe(topology_st): """Test that queries which are invalid, are correctly marked as "notes=F" in the access log, and return no entries or partial sets. :id: 8b2b23fe-d878-435c-bc84-8c298be4ca1f :setup: Standalone instance :steps: 1. Search a well formed query 2. Search a poorly formed query 3. Search a poorly formed complex (and/or) query :expectedresults: 1. No warnings 2. notes=F is present 3. notes=F is present """ inst = topology_st.standalone # In case the default has changed, we set the value to warn. inst.config.set("nsslapd-verify-filter-schema", "process-safe") # Set the access log to un-buffered so we get it immediately. inst.config.set("nsslapd-accesslog-logbuffering", "off") # Setup the query object. # Now we don't care if there are any results, we only care about good/bad queries. # To do this we have to bypass some of the lib389 magic, and just emit raw queries # to check them. Turns out lib389 is well designed and this just works as expected # if you use a single DSLdapObjects and filter. :) raw_objects = DSLdapObjects(inst, basedn=DEFAULT_SUFFIX) # Find any initial notes=F access_log = DirsrvAccessLog(inst) r_init = access_log.match(".*notes=F.*") # Check a good query has no warnings. r = raw_objects.filter("(objectClass=*)") assert(len(r) > 0) r_s1 = access_log.match(".*notes=F.*") # Should be the same number of log lines IE 0. assert(len(r_init) == len(r_s1)) # Check a bad one DOES emit a warning. r = raw_objects.filter("(a=a)") assert(len(r) == 0) r_s2 = access_log.match(".*notes=F.*") # Should be the greate number of log lines IE +1 assert(len(r_init) + 1 == len(r_s2)) # Check a bad complex one does emit a warning. r = raw_objects.filter("(&(a=a)(b=b)(objectClass=*))") assert(len(r) == 0) r_s3 = access_log.match(".*notes=F.*") # Should be the greate number of log lines IE +2 assert(len(r_init) + 2 == len(r_s3)) # Check that we can still get things when partial r = raw_objects.filter("(|(a=a)(b=b)(uid=foo))") assert(len(r) == 1) r_s4 = access_log.match(".*notes=F.*") # Should be the greate number of log lines IE +2 assert(len(r_init) + 3 == len(r_s4))
def test_indexing_is_online(topo): """Test that the changenmumber index is online right after enabling the plugin :id: 16f4c001-9e0c-4448-a2b3-08ac1e85d40f :setup: Standalone Instance :steps: 1. Enable retro cl 2. Perform some updates 3. Search for "(changenumber>=-1)", and it is not partially unindexed 4. Search for "(&(changenumber>=-1)(targetuniqueid=*))", and it is not partially unindexed :expectedresults: 1. Success 2. Success 3. Success 4. Success """ # Enable plugin topo.standalone.config.set('nsslapd-accesslog-logbuffering', 'off') plugin = RetroChangelogPlugin(topo.standalone) plugin.enable() topo.standalone.restart() # Do a bunch of updates users = UserAccounts(topo.standalone, DEFAULT_SUFFIX) user_entry = users.create( properties={ 'sn': '1', 'cn': 'user 1', 'uid': 'user1', 'uidNumber': '11', 'gidNumber': '111', 'givenname': 'user1', 'homePhone': '0861234567', 'carLicense': '131D16674', 'mail': '*****@*****.**', 'homeDirectory': '/home' }) for count in range(0, 10): user_entry.replace('mail', f'test{count}@test.com') # Search the retro cl, and check for error messages filter_simple = '(changenumber>=-1)' filter_compound = '(&(changenumber>=-1)(targetuniqueid=*))' retro_changelog_suffix = DSLdapObjects(topo.standalone, basedn=RETROCL_SUFFIX) retro_changelog_suffix.filter(filter_simple) assert not topo.standalone.searchAccessLog('Partially Unindexed Filter') # Search the retro cl again with compound filter retro_changelog_suffix.filter(filter_compound) assert not topo.standalone.searchAccessLog('Partially Unindexed Filter')
def test_schema_operation(topo): """Test that the cases in original schema are preserved. Test that duplicated schema except cases are not loaded Test to use a custom schema :id: e7448863-ac62-4b49-b013-4efa412c0455 :setup: Standalone instance :steps: 1. Create a test schema with cases 2. Run a schema_reload task 3. Check the attribute is present 4. Case 2: Check duplicated schema except cases are not loaded 5. Case 2-1: Use the custom schema :expectedresults: 1. Operation should be successful 2. Operation should be successful 3. Operation should be successful 4. Operation should be successful 5. Operation should be successful """ log.info('case 1: Test the cases in the original schema are preserved.') schema_filename = topo.standalone.schemadir + '/98test.ldif' try: with open(schema_filename, "w") as schema_file: schema_file.write("dn: cn=schema\n") schema_file.write( "attributetypes: ( 8.9.10.11.12.13.14 NAME " + "'MoZiLLaaTTRiBuTe' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " + " X-ORIGIN 'Mozilla Dummy Schema' )\n") schema_file.write( "objectclasses: ( 1.2.3.4.5.6.7 NAME 'MozillaObject' " + "SUP top MUST ( objectclass $ cn ) MAY ( MoZiLLaaTTRiBuTe )" + " X-ORIGIN 'user defined' )')\n") except OSError as e: log.fatal("Failed to create schema file: " + "{} Error: {}".format(schema_filename, str(e))) # run the schema reload task with the default schemadir schema = Schema(topo.standalone) task = schema.reload(schema_dir=topo.standalone.schemadir) task.wait() subschema = topo.standalone.schema.get_subschema() at_obj = subschema.get_obj(ldap.schema.AttributeType, 'MoZiLLaaTTRiBuTe') assert at_obj is not None, "The attribute was not found on server" log.info('Case 2: Duplicated schema except cases are not loaded.') schema_filename = topo.standalone.schemadir + '/97test.ldif' try: with open(schema_filename, "w") as schema_file: Mozattr1 = "MOZILLAATTRIBUTE" schema_file.write("dn: cn=schema\n") schema_file.write( "attributetypes: ( 8.9.10.11.12.13.14 NAME " + "'MOZILLAATTRIBUTE' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " + "X-ORIGIN 'Mozilla Dummy Schema' )\n") schema_file.write( "objectclasses: ( 1.2.3.4.5.6.7 NAME 'MozillaObject' " + "SUP top MUST ( objectclass $ cn ) MAY ( MOZILLAATTRIBUTE ) " + "X-ORIGIN 'user defined' )')\n") except OSError as e: log.fatal("Failed to create schema file: " + "{} Error: {}".format(schema_filename, str(e))) # run the schema reload task with the default schemadir task = schema.reload(schema_dir=topo.standalone.schemadir) task.wait() subschema_duplicate = topo.standalone.schema.get_subschema() at_obj_duplicate = subschema_duplicate.get_obj(ldap.schema.AttributeType, 'MOZILLAATTRIBUTE') moz = re.findall('MOZILLAATTRIBUTE', str(at_obj_duplicate)) if moz: log.error( 'case 2: MOZILLAATTRIBUTE is in the objectclasses list -- FAILURE') assert False else: log.info( 'case 2: MOZILLAATTRIBUTE is not in the objectclasses list -- PASS' ) Mozattr2 = "mozillaattribute" log.info(f'Case 2-1: Use the custom schema with {Mozattr2}') name = "test_user" ld = ldap.initialize(topo.standalone.get_ldap_uri()) ld.simple_bind_s(DN_DM, PW_DM) ld.add_s(f"cn={name},{DEFAULT_SUFFIX}", [('objectclass', [b'top', b'person', b'MozillaObject']), ('sn', [ensure_bytes(name)]), ('cn', [ensure_bytes(name)]), (Mozattr2, [ensure_bytes(name)])]) mozattrval = DSLdapObjects( topo.standalone, DEFAULT_SUFFIX).filter('(objectclass=mozillaobject)')[0] assert mozattrval.get_attr_val_utf8('mozillaattribute') == name
def test_retrocl_exclude_attr_add(topology_st): """ Test exclude attribute feature of the retrocl plugin for add operation :id: 3481650f-2070-45ef-9600-2500cfc51559 :setup: Standalone instance :steps: 1. Enable dynamic plugins 2. Confige retro changelog plugin 3. Add an entry 4. Ensure entry attrs are in the changelog 5. Exclude an attr 6. Add another entry 7. Ensure excluded attr is not in the changelog :expectedresults: 1. Success 2. Success 3. Success 4. Success 5. Success 6. Success 7. Success """ st = topology_st.standalone log.info('Configure retrocl plugin') rcl = RetroChangelogPlugin(st) rcl.disable() rcl.enable() rcl.replace('nsslapd-attribute', 'nsuniqueid:targetUniqueId') log.info('Restarting instance') try: st.restart() except ldap.LDAPError as e: ldap.error('Failed to restart instance ' + e.args[0]['desc']) assert False users = UserAccounts(st, DEFAULT_SUFFIX) log.info('Adding user1') try: users.create( properties={ 'sn': '1', 'cn': 'user 1', 'uid': 'user1', 'uidNumber': '11', 'gidNumber': '111', 'givenname': 'user1', 'homePhone': '0861234567', 'carLicense': '131D16674', 'mail': '*****@*****.**', 'homeDirectory': '/home/user1', 'userpassword': USER_PW }) except ldap.ALREADY_EXISTS: pass except ldap.LDAPError as e: log.error("Failed to add user1: " + str(e)) log.info( 'Verify homePhone and carLicense attrs are in the changelog changestring' ) try: retro_changelog_suffix = DSLdapObjects(st, basedn=RETROCL_SUFFIX) cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') except ldap.LDAPError as e: log.fatal("Changelog search failed, error: " + str(e)) assert False assert len(cllist) > 0 if cllist[0].present('changes'): clstr = str(cllist[0].get_attr_vals_utf8('changes')) assert ATTR_HOMEPHONE in clstr assert ATTR_CARLICENSE in clstr log.info('Excluding attribute ' + ATTR_HOMEPHONE) args = FakeArgs() args.connections = [ st.host + ':' + str(st.port) + ':' + DN_DM + ':' + PW_DM ] args.instance = 'standalone1' args.basedn = None args.binddn = None args.starttls = False args.pwdfile = None args.bindpw = None args.prompt = False args.exclude_attrs = ATTR_HOMEPHONE args.func = retrochangelog_add dsrc_inst = dsrc_arg_concat(args, None) inst = connect_instance(dsrc_inst, False, args) result = args.func(inst, None, log, args) disconnect_instance(inst) assert result is None log.info('Restarting instance') try: st.restart() except ldap.LDAPError as e: ldap.error('Failed to restart instance ' + e.args[0]['desc']) assert False log.info('Adding user2') try: users.create( properties={ 'sn': '2', 'cn': 'user 2', 'uid': 'user2', 'uidNumber': '22', 'gidNumber': '222', 'givenname': 'user2', 'homePhone': '0879088363', 'carLicense': '04WX11038', 'mail': '*****@*****.**', 'homeDirectory': '/home/user2', 'userpassword': USER_PW }) except ldap.ALREADY_EXISTS: pass except ldap.LDAPError as e: log.error("Failed to add user2: " + str(e)) log.info('Verify homePhone attr is not in the changelog changestring') try: cllist = retro_changelog_suffix.filter(f'(targetDn={USER2_DN})') assert len(cllist) > 0 if cllist[0].present('changes'): clstr = str(cllist[0].get_attr_vals_utf8('changes')) assert ATTR_HOMEPHONE not in clstr assert ATTR_CARLICENSE in clstr except ldap.LDAPError as e: log.fatal("Changelog search failed, error: " + str(e)) assert False
def test_retrocl_exclude_attr_mod(topology_st): """ Test exclude attribute feature of the retrocl plugin for mod operation :id: f6bef689-685b-4f86-a98d-f7e6b1fcada3 :setup: Standalone instance :steps: 1. Enable dynamic plugins 2. Confige retro changelog plugin 3. Add user1 entry 4. Ensure entry attrs are in the changelog 5. Exclude an attr 6. Modify user1 entry 7. Ensure excluded attr is not in the changelog :expectedresults: 1. Success 2. Success 3. Success 4. Success 5. Success 6. Success 7. Success """ st = topology_st.standalone log.info('Configure retrocl plugin') rcl = RetroChangelogPlugin(st) rcl.disable() rcl.enable() rcl.replace('nsslapd-attribute', 'nsuniqueid:targetUniqueId') log.info('Restarting instance') try: st.restart() except ldap.LDAPError as e: ldap.error('Failed to restart instance ' + e.args[0]['desc']) assert False users = UserAccounts(st, DEFAULT_SUFFIX) log.info('Adding user1') try: user1 = users.create( properties={ 'sn': '1', 'cn': 'user 1', 'uid': 'user1', 'uidNumber': '11', 'gidNumber': '111', 'givenname': 'user1', 'homePhone': '0861234567', 'carLicense': '131D16674', 'mail': '*****@*****.**', 'homeDirectory': '/home/user1', 'userpassword': USER_PW }) except ldap.ALREADY_EXISTS: user1 = UserAccount(st, dn=USER1_DN) except ldap.LDAPError as e: log.error("Failed to add user1: " + str(e)) log.info( 'Verify homePhone and carLicense attrs are in the changelog changestring' ) try: retro_changelog_suffix = DSLdapObjects(st, basedn=RETROCL_SUFFIX) cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') except ldap.LDAPError as e: log.fatal("Changelog search failed, error: " + str(e)) assert False assert len(cllist) > 0 if cllist[0].present('changes'): clstr = str(cllist[0].get_attr_vals_utf8('changes')) assert ATTR_HOMEPHONE in clstr assert ATTR_CARLICENSE in clstr log.info('Excluding attribute ' + ATTR_CARLICENSE) args = FakeArgs() args.connections = [ st.host + ':' + str(st.port) + ':' + DN_DM + ':' + PW_DM ] args.instance = 'standalone1' args.basedn = None args.binddn = None args.starttls = False args.pwdfile = None args.bindpw = None args.prompt = False args.exclude_attrs = ATTR_CARLICENSE args.func = retrochangelog_add dsrc_inst = dsrc_arg_concat(args, None) inst = connect_instance(dsrc_inst, False, args) result = args.func(inst, None, log, args) disconnect_instance(inst) assert result is None log.info('Restarting instance') try: st.restart() except ldap.LDAPError as e: ldap.error('Failed to restart instance ' + e.args[0]['desc']) assert False log.info('Modify user1 carLicense attribute') try: user1.replace(ATTR_CARLICENSE, "123WX321") except ldap.LDAPError as e: log.fatal( 'test_retrocl_exclude_attr_mod: Failed to update user1 attribute: error ' + e.message['desc']) assert False log.info('Verify carLicense attr is not in the changelog changestring') try: cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') assert len(cllist) > 0 # There will be 2 entries in the changelog for this user, we are only #interested in the second one, the modify operation. if cllist[1].present('changes'): clstr = str(cllist[1].get_attr_vals_utf8('changes')) assert ATTR_CARLICENSE not in clstr except ldap.LDAPError as e: log.fatal("Changelog search failed, error: " + str(e)) assert False
def test_tombstone_cleanup(plugin): """ Assert that the USN plugin removes tombstone entries when the cleanup task is run. Test removal for a specific backend, a specific suffix, and up to a given USN number. """ # create a new backend and a new sub-suffix stored in the new backend ou_value = "People3" ou_suffix = DEFAULT_SUFFIX plugin._instance.backends.create(None, properties={ BACKEND_NAME: "People3Data", BACKEND_SUFFIX: "ou=" + ou_value + "," + ou_suffix, }) ou2 = create_test_ou(plugin._instance, ou=ou_value, suffix=ou_suffix) tombstones_b1 = DSLdapObjects(plugin._instance) tombstones_b1._basedn = "ou=People," + DEFAULT_SUFFIX tombstones_b1._objectclasses = ['nsTombstone'] tombstones_b2 = DSLdapObjects(plugin._instance) tombstones_b2._basedn = ou2.dn tombstones_b2._objectclasses = ['nsTombstone'] root_dse = RootDSE(plugin._instance) lastusn_b1 = lambda: root_dse.get_attr_val_int("lastusn;userroot") assert lastusn_b1() == -1 user1_b1 = create_test_user(plugin._instance) user2_b1 = create_test_user(plugin._instance) user3_b1 = create_test_user(plugin._instance) user1_b2 = create_test_user(plugin._instance, suffix=ou2.dn) user2_b2 = create_test_user(plugin._instance, suffix=ou2.dn) # assert no tombstones exist at this point assert not tombstones_b1.list() assert not tombstones_b2.list() # create 3 tombstone entries on default backend user1_b1.delete() user2_b1.delete() user3_b1.delete() # assert there are 3 tombstone entries indeed on default backend assert len(tombstones_b1.list()) == 3 assert not tombstones_b2.list() assert lastusn_b1() == 5 # remove all tombstone entries from default backend, with a USN value up to 4 task = plugin.cleanup(suffix=DEFAULT_SUFFIX, max_usn=lastusn_b1() - 1) task.wait() # assert all tombstone entries were deleted but the last one on default backend assert len(tombstones_b1.list()) == 1 assert not tombstones_b2.list() # create 2 tombstone entries on new backend user1_b2.delete() user2_b2.delete() # assert there are 2 tombstone entries indeed on new backend assert len(tombstones_b2.list()) == 2 assert len(tombstones_b1.list()) == 1 # remove all tombstone entries from ou2 suffix task = plugin.cleanup(suffix=ou2.dn) task.wait() # assert there are no tombstone entries stored on ou2 suffix assert not tombstones_b2.list() assert len(tombstones_b1.list()) == 1 # reset USN for subsequent test cases b = Backend(plugin._instance, dn="cn=people3data," + DN_LDBM) reset_USN(plugin, [ou2, b])