From 0fd2eff537d1072dc81b97ae088e1a493a2dd8d1 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Thu, 21 Sep 2017 10:19:03 -0400 Subject: [PATCH] Ticket 49327 - Add CI test for password expiration controls Description: Add CI script for testing the various expired/expiring controls. https://pagure.io/389-ds-base/issue/49327 Reviewed by: ? --- .../suites/password/pwdPolicy_controls_test.py | 324 +++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 dirsrvtests/tests/suites/password/pwdPolicy_controls_test.py diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_controls_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_controls_test.py new file mode 100644 index 0000000..ccd379d --- /dev/null +++ b/dirsrvtests/tests/suites/password/pwdPolicy_controls_test.py @@ -0,0 +1,324 @@ +import logging +import pytest +import os +import ldap +import time +from ldap.controls.ppolicy import PasswordPolicyControl +from lib389.topologies import topology_st as topo +from lib389._constants import (DN_DM, PASSWORD, DN_CONFIG) +from lib389.tasks import Entry + +DEBUGGING = os.getenv("DEBUGGING", default=False) +if DEBUGGING: + logging.getLogger(__name__).setLevel(logging.DEBUG) +else: + logging.getLogger(__name__).setLevel(logging.INFO) +log = logging.getLogger(__name__) + +USER_DN = 'uid=test entry,dc=example,dc=com' +USER_PW = 'password123' + + +@pytest.fixture +def init_user(topo, request): + """Initialize a user - Delete and re-add test user + """ + try: + topo.standalone.simple_bind_s(DN_DM, PASSWORD) + topo.standalone.delete_s(USER_DN) + except ldap.NO_SUCH_OBJECT: + pass + except ldap.LDAPError as e: + log.error("Failed to delete user, error: {}".format(e.message['desc'])) + assert False + + user_data = {'objectClass': 'top person inetOrgPerson'.split(), + 'uid': 'test entry', + 'cn': 'test entry', + 'sn': 'user', + 'userPassword': USER_PW} + try: + topo.standalone.add_s(Entry((USER_DN, user_data))) + except ldap.LDAPError as e: + log.error("Failed to add user, error: {}".format(e.message['desc'])) + assert False + + +def change_passwd(topo): + """Reset users password as the user, then re-bind as Directory Manager + """ + try: + topo.standalone.simple_bind_s(USER_DN, USER_PW) + topo.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE, + 'userpassword', + USER_PW)]) + topo.standalone.simple_bind_s(DN_DM, PASSWORD) + except ldap.LDAPError as e: + log.error("Failed to change user's password, error: {}".format(e.message['desc'])) + assert False + + +def bind_and_get_control(topo, err=0): + """Bind as the user, and return any controls + """ + res_type = res_data = res_msgid = res_ctrls = None + result_id = '' + + try: + result_id = topo.standalone.simple_bind(USER_DN, USER_PW, + serverctrls=[PasswordPolicyControl()]) + res_type, res_data, res_msgid, res_ctrls = topo.standalone.result3(result_id) + if err: + log.fatal('Expected an error, but bind succeeded') + assert False + except ldap.LDAPError as e: + if err: + log.debug('Got expected error: {}'.format(e.message['desc'])) + pass + else: + log.fatal('Did not expect an error: {}'.format(e.message['desc'])) + assert False + + if DEBUGGING and res_ctrls and len(res_ctrls) > 0: + for ctl in res_ctrls: + if ctl.timeBeforeExpiration: + log.debug('control time before expiration: {}'.format(ctl.timeBeforeExpiration)) + if ctl.graceAuthNsRemaining: + log.debug('control grace login remaining: {}'.format(ctl.graceAuthNsRemaining)) + if ctl.error is not None and ctl.error >= 0: + log.debug('control error: {}'.format(ctl.error)) + + topo.standalone.simple_bind_s(DN_DM, PASSWORD) + return res_ctrls + + +def test_pwd_must_change(topo, init_user): + """Test for expiration control when password must be changed because an + admin reset the password + + :id: a3d99be5-0b69-410d-b72f-04eda8821a56 + :setup: Standalone instance, a user for testing + :steps: + 1. Configure password policy and reset password as admin + 2. Bind, and check for expired control withthe proper error code "2" + :expectedresults: + 1. Config update succeeds, adn the password is reset + 2. The EXPIRED control is returned, and we the expected error code "2" + """ + + log.info('Configure password policy with paswordMustChange set to "on"') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordExp', 'on'), + (ldap.MOD_REPLACE, 'passwordMaxAge', '200'), + (ldap.MOD_REPLACE, 'passwordGraceLimit', '0'), + (ldap.MOD_REPLACE, 'passwordWarning', '199'), + (ldap.MOD_REPLACE, 'passwordMustChange', 'on')]) + except ldap.LDAPError as e: + log.error("Failed to set password policy, error: {}".format(e.message['desc'])) + assert False + + log.info('Reset userpassword as Directory Manager') + try: + topo.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE, + 'userpassword', + USER_PW)]) + except ldap.LDAPError as e: + log.error("Failed to change user's password, error: {}".format(e.message['desc'])) + assert False + + log.info('Bind should return ctrl with error code 2 (changeAfterReset)') + time.sleep(2) + ctrls = bind_and_get_control(topo) + if ctrls and len(ctrls) > 0: + if ctrls[0].error is None: + log.fatal("Response ctrl error code not set") + assert False + elif ctrls[0].error != 2: + log.fatal("Got unexpected error code: {}".format(ctrls[0].error)) + assert False + else: + log.fatal("We did not get a response ctrl") + assert False + + +def test_pwd_expired_grace_limit(topo, init_user): + """Test for expiration control when password is expired, but there are + remaining grace logins + + :id: a3d99be5-0b69-410d-b72f-04eda8821a51 + :setup: Standalone instance, a user for testing + :steps: + 1. Configure password policy and reset password,adn allow it to expire + 2. Bind, and check for expired control, and grace limit + 3. Bind again, consuming the last grace login, control should be returned + 4. Bind again, it should fail, and no control returned + :expectedresults: + 1. Config update and password reset are successful + 2. The EXPIRED control is returned, and we get the expected number + of grace logins in the control + 3. The response control has the expected value for grace logins + 4. The bind fails with error 49, and no contorl is returned + """ + + log.info('Configure password policy with grace limit set tot 2') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordExp', 'on'), + (ldap.MOD_REPLACE, 'passwordMaxAge', '5'), + (ldap.MOD_REPLACE, 'passwordGraceLimit', '2')]) + except ldap.LDAPError as e: + log.error("Failed to set password policy, error: {}".format(e.message['desc'])) + assert False + + log.info('Change password and wait for it to expire') + change_passwd(topo) + time.sleep(6) + + log.info('Bind and use up one grace login (only one left)') + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get EXPIRED control in resposne') + assert False + else: + if int(ctrls[0].graceAuthNsRemaining) != 1: + log.fatal('Got unexpected value for grace logins: {}'.format(ctrls[0].graceAuthNsRemaining)) + assert False + + log.info('Use up last grace login, should get control') + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get control in response') + assert False + + log.info('No grace login available, bind should fail, and no control should be returned') + ctrls = bind_and_get_control(topo, err=49) + if ctrls and len(ctrls) > 0: + log.fatal('Incorrectly got control in response') + assert False + + +def test_pwd_expiring_with_warning(topo, init_user): + """Test expiring control response before and after warning is sent + + :id: a3d99be5-0b69-410d-b72f-04eda8821a54 + :setup: Standalone instance, a user for testing + :steps: + 1. Configure password policy, and reset password + 2. Check for EXPIRING control, and the "time to expire" + 3. Bind again, as a warning has now been sent, and check the "time to expire" + :expectedresults: + 1. Configuration update and password reset are successful + 2. Get the EXPIRING control, and the expected "time to expire" values + 3. Get the EXPIRING control, and the expected "time to expire" values + """ + + log.info('Configure password policy') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordExp', 'on'), + (ldap.MOD_REPLACE, 'passwordMaxAge', '50'), + (ldap.MOD_REPLACE, 'passwordWarning', '50')]) + except ldap.LDAPError as e: + log.error("Failed to set password policy, error: {}".format(e.message['desc'])) + assert False + + log.info('Change password and get controls') + change_passwd(topo) + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get EXPIRING control in response') + assert False + + if int(ctrls[0].timeBeforeExpiration) < 50: + log.fatal('Got unexpected value for timeBeforeExpiration: {}'.format(ctrls[0].timeBeforeExpiration)) + assert False + + log.info('Warning has been sent, try the bind again, and recheck the expiring time') + time.sleep(5) + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get EXPIRING control in resposne') + assert False + + if int(ctrls[0].timeBeforeExpiration) > 50: + log.fatal('Got unexpected value for timeBeforeExpiration: {}'.format(ctrls[0].timeBeforeExpiration)) + assert False + + +def test_pwd_expiring_with_no_warning(topo, init_user): + """Test expiring control response when no warning is sent + + :id: a3d99be5-0b69-410d-b72f-04eda8821a54 + :setup: Standalone instance, a user for testing + :steps: + 1. Configure password policy, and reset password + 2. Bind, and check that no controls are returned + 3. Set passwordSendExpiringTime to "on", bind, and check that the + EXPIRING control is returned + :expectedresults: + 1. Configuration update and passwordreset are successful + 2. No control is returned from bind + 3. A control is returned after setting "passwordSendExpiringTime" + """ + + log.info('Configure password policy') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordExp', 'on'), + (ldap.MOD_REPLACE, 'passwordMaxAge', '50'), + (ldap.MOD_REPLACE, 'passwordWarning', '5')]) + except ldap.LDAPError as e: + log.error("Failed to set password policy, error: {}".format(e.message['desc'])) + assert False + + log.info('When the warning is less than the max age, we never send expiring control response') + change_passwd(topo) + ctrls = bind_and_get_control(topo) + if len(ctrls) > 0: + log.fatal('Incorrectly got a response control: {}'.format(ctrls)) + assert False + + log.info('Turn on sending expiring control regardless of warning') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordSendExpiringTime', 'on')]) + except ldap.LDAPError as e: + log.error("Failed to set passwordSendExpiringTime, error: {}".format(e.message['desc'])) + assert False + + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get EXPIRED control in response') + assert False + + if int(ctrls[0].timeBeforeExpiration) < 50: + log.fatal('Got unexpected value for time before expiration: {}'.format(ctrls[0].timeBeforeExpiration)) + assert False + + log.info('Check expiring time again') + time.sleep(6) + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get EXPIRED control in resposne') + assert False + + if int(ctrls[0].timeBeforeExpiration) > 50: + log.fatal('Got unexpected value for time before expiration: {}'.format(ctrls[0].timeBeforeExpiration)) + assert False + + log.info('Turn off sending expiring control (restore the default setting)') + try: + topo.standalone.modify_s(DN_CONFIG, [ + (ldap.MOD_REPLACE, 'passwordSendExpiringTime', 'off')]) + except ldap.LDAPError as e: + log.error("Failed to set passwordSendExpiringTime, error: {}".format(e.message['desc'])) + assert False + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) + -- 2.9.5