pwdPolicy_controls_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import logging
  2. import pytest
  3. import os
  4. import ldap
  5. import time
  6. from ldap.controls.ppolicy import PasswordPolicyControl
  7. from lib389.topologies import topology_st as topo
  8. from lib389.idm.user import UserAccounts
  9. from lib389._constants import (DN_DM, PASSWORD, DEFAULT_SUFFIX)
  10. from lib389.idm.organizationalunit import OrganizationalUnits
  11. pytestmark = pytest.mark.tier1
  12. DEBUGGING = os.getenv("DEBUGGING", default=False)
  13. if DEBUGGING:
  14. logging.getLogger(__name__).setLevel(logging.DEBUG)
  15. else:
  16. logging.getLogger(__name__).setLevel(logging.INFO)
  17. log = logging.getLogger(__name__)
  18. USER_DN = 'uid=test entry,ou=people,dc=example,dc=com'
  19. USER_PW = b'password123'
  20. USER_ACI = '(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)'
  21. @pytest.fixture
  22. def init_user(topo, request):
  23. """Initialize a user - Delete and re-add test user
  24. """
  25. try:
  26. topo.standalone.simple_bind_s(DN_DM, PASSWORD)
  27. users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
  28. user = users.get('test entry')
  29. user.delete()
  30. except ldap.NO_SUCH_OBJECT:
  31. pass
  32. except ldap.LDAPError as e:
  33. log.error("Failed to delete user, error: {}".format(e.message['desc']))
  34. assert False
  35. user_data = {'uid': 'test entry',
  36. 'cn': 'test entry',
  37. 'sn': 'test entry',
  38. 'uidNumber': '3000',
  39. 'gidNumber': '4000',
  40. 'homeDirectory': '/home/test_entry',
  41. 'userPassword': USER_PW}
  42. users.create(properties=user_data)
  43. def change_passwd(topo):
  44. """Reset users password as the user, then re-bind as Directory Manager
  45. """
  46. users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
  47. user = users.get('test entry')
  48. user.rebind(USER_PW)
  49. user.reset_password(USER_PW)
  50. topo.standalone.simple_bind_s(DN_DM, PASSWORD)
  51. def bind_and_get_control(topo, err=0):
  52. """Bind as the user, and return any controls
  53. """
  54. res_type = res_data = res_msgid = res_ctrls = None
  55. result_id = ''
  56. try:
  57. result_id = topo.standalone.simple_bind(USER_DN, USER_PW,
  58. serverctrls=[PasswordPolicyControl()])
  59. res_type, res_data, res_msgid, res_ctrls = topo.standalone.result3(result_id)
  60. if err:
  61. log.fatal('Expected an error, but bind succeeded')
  62. assert False
  63. except ldap.LDAPError as e:
  64. if err:
  65. log.debug('Got expected error: {}'.format(str(e)))
  66. pass
  67. else:
  68. log.fatal('Did not expect an error: {}'.format(str(e)))
  69. assert False
  70. if DEBUGGING and res_ctrls and len(res_ctrls) > 0:
  71. for ctl in res_ctrls:
  72. if ctl.timeBeforeExpiration:
  73. log.debug('control time before expiration: {}'.format(ctl.timeBeforeExpiration))
  74. if ctl.graceAuthNsRemaining:
  75. log.debug('control grace login remaining: {}'.format(ctl.graceAuthNsRemaining))
  76. if ctl.error is not None and ctl.error >= 0:
  77. log.debug('control error: {}'.format(ctl.error))
  78. topo.standalone.simple_bind_s(DN_DM, PASSWORD)
  79. return res_ctrls
  80. def test_pwd_must_change(topo, init_user):
  81. """Test for expiration control when password must be changed because an
  82. admin reset the password
  83. :id: a3d99be5-0b69-410d-b72f-04eda8821a56
  84. :setup: Standalone instance, a user for testing
  85. :steps:
  86. 1. Configure password policy and reset password as admin
  87. 2. Bind, and check for expired control withthe proper error code "2"
  88. :expectedresults:
  89. 1. Config update succeeds, adn the password is reset
  90. 2. The EXPIRED control is returned, and we the expected error code "2"
  91. """
  92. log.info('Configure password policy with paswordMustChange set to "on"')
  93. topo.standalone.config.set('passwordExp', 'on')
  94. topo.standalone.config.set('passwordMaxAge', '200')
  95. topo.standalone.config.set('passwordGraceLimit', '0')
  96. topo.standalone.config.set('passwordWarning', '199')
  97. topo.standalone.config.set('passwordMustChange', 'on')
  98. ous = OrganizationalUnits(topo.standalone, DEFAULT_SUFFIX)
  99. ou = ous.get('people')
  100. ou.add('aci', USER_ACI)
  101. log.info('Reset userpassword as Directory Manager')
  102. users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
  103. user = users.get('test entry')
  104. user.reset_password(USER_PW)
  105. log.info('Bind should return ctrl with error code 2 (changeAfterReset)')
  106. time.sleep(2)
  107. ctrls = bind_and_get_control(topo)
  108. if ctrls and len(ctrls) > 0:
  109. if ctrls[0].error is None:
  110. log.fatal("Response ctrl error code not set")
  111. assert False
  112. elif ctrls[0].error != 2:
  113. log.fatal("Got unexpected error code: {}".format(ctrls[0].error))
  114. assert False
  115. else:
  116. log.fatal("We did not get a response ctrl")
  117. assert False
  118. def test_pwd_expired_grace_limit(topo, init_user):
  119. """Test for expiration control when password is expired, but there are
  120. remaining grace logins
  121. :id: a3d99be5-0b69-410d-b72f-04eda8821a51
  122. :setup: Standalone instance, a user for testing
  123. :steps:
  124. 1. Configure password policy and reset password,adn allow it to expire
  125. 2. Bind, and check for expired control, and grace limit
  126. 3. Bind again, consuming the last grace login, control should be returned
  127. 4. Bind again, it should fail, and no control returned
  128. :expectedresults:
  129. 1. Config update and password reset are successful
  130. 2. The EXPIRED control is returned, and we get the expected number
  131. of grace logins in the control
  132. 3. The response control has the expected value for grace logins
  133. 4. The bind fails with error 49, and no contorl is returned
  134. """
  135. log.info('Configure password policy with grace limit set tot 2')
  136. topo.standalone.config.set('passwordExp', 'on')
  137. topo.standalone.config.set('passwordMaxAge', '5')
  138. topo.standalone.config.set('passwordGraceLimit', '2')
  139. log.info('Change password and wait for it to expire')
  140. change_passwd(topo)
  141. time.sleep(6)
  142. log.info('Bind and use up one grace login (only one left)')
  143. ctrls = bind_and_get_control(topo)
  144. if ctrls is None or len(ctrls) == 0:
  145. log.fatal('Did not get EXPIRED control in resposne')
  146. assert False
  147. else:
  148. if int(ctrls[0].graceAuthNsRemaining) != 1:
  149. log.fatal('Got unexpected value for grace logins: {}'.format(ctrls[0].graceAuthNsRemaining))
  150. assert False
  151. log.info('Use up last grace login, should get control')
  152. ctrls = bind_and_get_control(topo)
  153. if ctrls is None or len(ctrls) == 0:
  154. log.fatal('Did not get control in response')
  155. assert False
  156. log.info('No grace login available, bind should fail, and no control should be returned')
  157. ctrls = bind_and_get_control(topo, err=49)
  158. if ctrls and len(ctrls) > 0:
  159. log.fatal('Incorrectly got control in response')
  160. assert False
  161. def test_pwd_expiring_with_warning(topo, init_user):
  162. """Test expiring control response before and after warning is sent
  163. :id: a3d99be5-0b69-410d-b72f-04eda8821a54
  164. :setup: Standalone instance, a user for testing
  165. :steps:
  166. 1. Configure password policy, and reset password
  167. 2. Check for EXPIRING control, and the "time to expire"
  168. 3. Bind again, as a warning has now been sent, and check the "time to expire"
  169. :expectedresults:
  170. 1. Configuration update and password reset are successful
  171. 2. Get the EXPIRING control, and the expected "time to expire" values
  172. 3. Get the EXPIRING control, and the expected "time to expire" values
  173. """
  174. log.info('Configure password policy')
  175. topo.standalone.config.set('passwordExp', 'on')
  176. topo.standalone.config.set('passwordMaxAge', '50')
  177. topo.standalone.config.set('passwordWarning', '50')
  178. log.info('Change password and get controls')
  179. change_passwd(topo)
  180. ctrls = bind_and_get_control(topo)
  181. if ctrls is None or len(ctrls) == 0:
  182. log.fatal('Did not get EXPIRING control in response')
  183. assert False
  184. if int(ctrls[0].timeBeforeExpiration) < 50:
  185. log.fatal('Got unexpected value for timeBeforeExpiration: {}'.format(ctrls[0].timeBeforeExpiration))
  186. assert False
  187. log.info('Warning has been sent, try the bind again, and recheck the expiring time')
  188. time.sleep(5)
  189. ctrls = bind_and_get_control(topo)
  190. if ctrls is None or len(ctrls) == 0:
  191. log.fatal('Did not get EXPIRING control in resposne')
  192. assert False
  193. if int(ctrls[0].timeBeforeExpiration) > 50:
  194. log.fatal('Got unexpected value for timeBeforeExpiration: {}'.format(ctrls[0].timeBeforeExpiration))
  195. assert False
  196. def test_pwd_expiring_with_no_warning(topo, init_user):
  197. """Test expiring control response when no warning is sent
  198. :id: a3d99be5-0b69-410d-b72f-04eda8821a54
  199. :setup: Standalone instance, a user for testing
  200. :steps:
  201. 1. Configure password policy, and reset password
  202. 2. Bind, and check that no controls are returned
  203. 3. Set passwordSendExpiringTime to "on", bind, and check that the
  204. EXPIRING control is returned
  205. :expectedresults:
  206. 1. Configuration update and passwordreset are successful
  207. 2. No control is returned from bind
  208. 3. A control is returned after setting "passwordSendExpiringTime"
  209. """
  210. log.info('Configure password policy')
  211. topo.standalone.config.set('passwordExp', 'on')
  212. topo.standalone.config.set('passwordMaxAge', '50')
  213. topo.standalone.config.set('passwordWarning', '5')
  214. log.info('When the warning is less than the max age, we never send expiring control response')
  215. change_passwd(topo)
  216. ctrls = bind_and_get_control(topo)
  217. if len(ctrls) > 0:
  218. log.fatal('Incorrectly got a response control: {}'.format(ctrls))
  219. assert False
  220. log.info('Turn on sending expiring control regardless of warning')
  221. topo.standalone.config.set('passwordSendExpiringTime', 'on')
  222. ctrls = bind_and_get_control(topo)
  223. if ctrls is None or len(ctrls) == 0:
  224. log.fatal('Did not get EXPIRED control in response')
  225. assert False
  226. if int(ctrls[0].timeBeforeExpiration) < 49:
  227. log.fatal('Got unexpected value for time before expiration: {}'.format(ctrls[0].timeBeforeExpiration))
  228. assert False
  229. log.info('Check expiring time again')
  230. time.sleep(6)
  231. ctrls = bind_and_get_control(topo)
  232. if ctrls is None or len(ctrls) == 0:
  233. log.fatal('Did not get EXPIRED control in resposne')
  234. assert False
  235. if int(ctrls[0].timeBeforeExpiration) > 51:
  236. log.fatal('Got unexpected value for time before expiration: {}'.format(ctrls[0].timeBeforeExpiration))
  237. assert False
  238. log.info('Turn off sending expiring control (restore the default setting)')
  239. topo.standalone.config.set('passwordSendExpiringTime', 'off')
  240. if __name__ == '__main__':
  241. # Run isolated
  242. # -s for DEBUG mode
  243. CURRENT_FILE = os.path.realpath(__file__)
  244. pytest.main("-s %s" % CURRENT_FILE)