From 8c4010876d3e2233827342d192df3af18d06e107 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Wed, 20 Sep 2017 23:11:49 -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 | 322 +++++++++++++++++++++ 1 file changed, 322 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..5db98ee --- /dev/null +++ b/dirsrvtests/tests/suites/password/pwdPolicy_controls_test.py @@ -0,0 +1,322 @@ +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 = 'myTestPassword1' + + +@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 + :feature: Test the password policy expiration control after password reset + :setup: Configure password policy + :steps: + 1. Configure password policy and reset password as admin + 2. Bind, and check for expired control withthe proper error code "2" + :expectedresults: + 1. The EXPIRED control is returned, and we the expected error code + """ + # Configure password policy + 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 + + # Reset userpassword + 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 + + # 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 + :feature: Test the password policy expiration control with grace limits + :setup: Configure password exp, maxage, and grace logins + :steps: + 1. Configure password policy and reset password + 2. Allow password to expire + 3. Bind, and check for expired control, and grace limit + 4. Bind again, consuming the last grace login, control should be returned + 5. Bind again, it should fail, ano control returned + :expectedresults: + 1. The EXPIRED control is returned, and we the expected number of grace + logins in the control + """ + + # Configure password policy + 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 + + change_passwd(topo) + time.sleep(6) # password expired + + # 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 + + # Use up last grace login + ctrls = bind_and_get_control(topo) + if ctrls is None or len(ctrls) == 0: + log.fatal('Did not get control in response') + assert False + + # 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 + :feature: Password expiraton warning + :setup: Configure password policy exp, max age, adn warning + :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. Get the EXPIRING control, and the expected "time to expire" values + """ + + # 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 + + # 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 + + # Warning has been sent + 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 + :feature: Test the "passwordSendExpiringTime" setting, which allows the EXPIRING + control to be sent even if a warning has not been issued. + :setup: Congiure password policy so a warning will not be sent + :steps: + 1. Configure password policy, and reset password + 2. Bind, and check that no controls are returned + 3. Set "passwordSendExpiringTime" to "on" + 4. Bind, and check that the EXPIRING control is returned + 5. Reset "passwordSendExpiringTime" + :expectedresults: + 1. We don't get a control if a warning has not been sent + 2. We do get a control after setting "passwordSendExpiringTime" + """ + + # 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 + + # 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 + + # 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 + + # 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 + + # 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