From b4b846d3e7f31fe87988f2db5bd2964208d18c14 Mon Sep 17 00:00:00 2001 From: Thierry Bordaz Date: Thu, 12 Dec 2024 14:35:12 +0100 Subject: [PATCH] Issue 6446 - on replica consumer, account policy plugin fails to manage the last login history Bug description: An internal update needs the flag SLAPI_OP_FLAG_BYPASS_REFERRALS on a RO consumer. Else the server does not select the backend and returns LDAP_REFERRAL to the internal operation. In acct_update_login_history the flag is missing. Fix description: Use the same flags in acct_update_login_history as it is used in acct_record_login fixes: #6446 Reviewed by: Mark Reynolds (Thanks!) --- .../tests/suites/plugins/accpol_test.py | 123 +++++++++++++++++- ldap/servers/plugins/acctpolicy/acct_plugin.c | 3 +- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/dirsrvtests/tests/suites/plugins/accpol_test.py b/dirsrvtests/tests/suites/plugins/accpol_test.py index 964d98ee07..f5db8e4614 100644 --- a/dirsrvtests/tests/suites/plugins/accpol_test.py +++ b/dirsrvtests/tests/suites/plugins/accpol_test.py @@ -10,7 +10,7 @@ import subprocess from lib389.tasks import * from lib389.utils import * -from lib389.topologies import topology_st +from lib389.topologies import topology_st, topology_m1c1 from lib389.idm.user import (UserAccount, UserAccounts) from lib389.plugins import (AccountPolicyPlugin, AccountPolicyConfig, AccountPolicyConfigs) from lib389.cos import (CosTemplate, CosPointerDefinition) @@ -1298,6 +1298,127 @@ def test_locact_modrdn(topology_st, accpol_local): account_status(topology_st, suffix, subtree, userid, 1, 0, "Enabled") del_users(topology_st, suffix, subtree, userid, nousrs) +def test_acct_policy_consumer(topology_m1c1, request): + """Test the lastLoginHistory is updated on consumer without + referral error + + :id: 53a9a2c7-6b10-41c9-b9f9-bde412d04acb + :setup: Supplier Instance, Consumer Instance + :steps: + 1. Create a test entry on the supplier + 2. Configure lastLoginTime on supplier and consumer + 3. On supplier 3 binds of the test entry + 4. On supplier check there is 3 lastLoginHistory values + 5. On supplier check there is no error in error logs + 6. On consumer 3 binds of the test entry + 7. On consumer check there is 3 lastLoginHistory values + 8. On consumer check there is no error in error logs + :expectedresults: + 1. Success + 2. Success + 3. Success + 4. Success + 5. Success + 6. Success + 7. Success + 8. Success + """ + + supplier = topology_m1c1.ms['supplier1'] + consumer = topology_m1c1.cs['consumer1'] + USER_PW = 'password' + + # Add on the supplier a test user entry and wait it is on the consumer + users_supplier = UserAccounts(supplier, DEFAULT_SUFFIX, rdn=None) + user_supplier = users_supplier.create_test_user(uid=1000, gid=2000) + user_supplier.replace('userPassword', USER_PW) + + users_consumer = UserAccounts(consumer, DEFAULT_SUFFIX, rdn=None) + for i in range(0, 10): + try: + user_consumer = users_consumer.get("test_user_1000") + break + except ldap.NO_SUCH_OBJECT: + time.sleep(1) + + # Configure lastLoginTime on supplier and consumer + plugin = AccountPolicyPlugin(supplier) + plugin.enable() + ACCPOL_DN = "cn={},{}".format(PLUGIN_ACCT_POLICY, DN_PLUGIN) + ACCP_CONF = "{},{}".format(DN_CONFIG, ACCPOL_DN) + plugin.set('nsslapd-pluginarg0', ACCP_CONF) + accp = AccountPolicyConfig(supplier, dn=ACCP_CONF) + accp.set('alwaysrecordlogin', 'yes') + accp.set('stateattrname', 'lastLoginTime') + supplier.restart(timeout=10) + + plugin = AccountPolicyPlugin(consumer) + plugin.enable() + ACCPOL_DN = "cn={},{}".format(PLUGIN_ACCT_POLICY, DN_PLUGIN) + ACCP_CONF = "{},{}".format(DN_CONFIG, ACCPOL_DN) + plugin.set('nsslapd-pluginarg0', ACCP_CONF) + accp = AccountPolicyConfig(consumer, dn=ACCP_CONF) + accp.set('alwaysrecordlogin', 'yes') + accp.set('stateattrname', 'lastLoginTime') + consumer.restart(timeout=10) + + # On supplier + # Do 3 binds with a delay to + # allow several values lastLoginHistory + user_supplier.bind(USER_PW) + time.sleep(2) + user_supplier = users_supplier.get("test_user_1000") + user_supplier.bind(USER_PW) + time.sleep(2) + user_supplier = users_supplier.get("test_user_1000") + user_supplier.bind(USER_PW) + + # Verify there is no referral error + results = supplier.ds_error_log.match('.*.acct_update_login_history - Modify error 10 on entry*') + assert not results + + # Verify that we got 3 values for lastLoginHistory + assert len(user_supplier.get_attr_vals_utf8_l('lastLoginHistory')) == 3 + + # On Consumer + # Do 3 binds with a delay to + # allow several values lastLoginHistory + user_supplier.bind(USER_PW) + user_consumer.bind(USER_PW) + time.sleep(2) + user_consumer = users_supplier.get("test_user_1000") + user_consumer.bind(USER_PW) + time.sleep(2) + user_consumer = users_supplier.get("test_user_1000") + user_consumer.bind(USER_PW) + + # Verify there is no referral error + results = consumer.ds_error_log.match('.*.acct_update_login_history - Modify error 10 on entry*') + assert not results + + # Verify that we got at least 3 values for lastLoginHistory + assert len(user_consumer.get_attr_vals_utf8_l('lastLoginHistory')) >= 3 + + + def fin(): + user_supplier.delete() + log.info('Disabling Global accpolicy plugin and removing pwpolicy attrs') + try: + plugin = AccountPolicyPlugin(supplier) + plugin.disable() + except ldap.LDAPError as e: + log.error('Failed to disable Global accpolicy plugin, {}'.format(e.message['desc'])) + assert False + supplier.restart(timeout=10) + try: + plugin = AccountPolicyPlugin(consumer) + plugin.disable() + except ldap.LDAPError as e: + log.error('Failed to disable Global accpolicy plugin, {}'.format(e.message['desc'])) + assert False + consumer.restart(timeout=10) + + request.addfinalizer(fin) if __name__ == '__main__': # Run isolated diff --git a/ldap/servers/plugins/acctpolicy/acct_plugin.c b/ldap/servers/plugins/acctpolicy/acct_plugin.c index 220d0f6b26..af566b9349 100644 --- a/ldap/servers/plugins/acctpolicy/acct_plugin.c +++ b/ldap/servers/plugins/acctpolicy/acct_plugin.c @@ -290,7 +290,8 @@ acct_update_login_history(const char *dn, char *timestr) list_of_mods[1] = NULL; mod_pb = slapi_pblock_new(); - slapi_modify_internal_set_pb(mod_pb, dn, list_of_mods, NULL, NULL, plugin_id, 0); + slapi_modify_internal_set_pb(mod_pb, dn, list_of_mods, NULL, NULL, plugin_id, + SLAPI_OP_FLAG_NO_ACCESS_CHECK |SLAPI_OP_FLAG_BYPASS_REFERRALS); slapi_modify_internal_pb(mod_pb); slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); if (rc != LDAP_SUCCESS) {