1
0

ticket48228_test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2015 Red Hat, Inc.
  3. # All rights reserved.
  4. #
  5. # License: GPL (version 3 or any later version).
  6. # See LICENSE for details.
  7. # --- END COPYRIGHT BLOCK ---
  8. #
  9. import os
  10. import sys
  11. import time
  12. import ldap
  13. import logging
  14. import pytest
  15. from lib389 import DirSrv, Entry, tools, tasks
  16. from lib389.tools import DirSrvTools
  17. from lib389._constants import *
  18. from lib389.properties import *
  19. from lib389.tasks import *
  20. log = logging.getLogger(__name__)
  21. installation_prefix = None
  22. # Assuming DEFAULT_SUFFIX is "dc=example,dc=com", otherwise it does not work... :(
  23. SUBTREE_CONTAINER = 'cn=nsPwPolicyContainer,' + DEFAULT_SUFFIX
  24. SUBTREE_PWPDN = 'cn=nsPwPolicyEntry,' + DEFAULT_SUFFIX
  25. SUBTREE_PWP = 'cn=cn\3DnsPwPolicyEntry\2Cdc\3Dexample\2Cdc\3Dcom,' + SUBTREE_CONTAINER
  26. SUBTREE_COS_TMPLDN = 'cn=nsPwTemplateEntry,' + DEFAULT_SUFFIX
  27. SUBTREE_COS_TMPL = 'cn=cn\3DnsPwTemplateEntry\2Cdc\3Dexample\2Cdc\3Dcom,' + SUBTREE_CONTAINER
  28. SUBTREE_COS_DEF = 'cn=nsPwPolicy_CoS,' + DEFAULT_SUFFIX
  29. USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
  30. USER2_DN = 'uid=user2,' + DEFAULT_SUFFIX
  31. class TopologyStandalone(object):
  32. def __init__(self, standalone):
  33. standalone.open()
  34. self.standalone = standalone
  35. @pytest.fixture(scope="module")
  36. def topology(request):
  37. '''
  38. This fixture is used to standalone topology for the 'module'.
  39. '''
  40. global installation_prefix
  41. if installation_prefix:
  42. args_instance[SER_DEPLOYED_DIR] = installation_prefix
  43. standalone = DirSrv(verbose=False)
  44. # Args for the standalone instance
  45. args_instance[SER_HOST] = HOST_STANDALONE
  46. args_instance[SER_PORT] = PORT_STANDALONE
  47. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  48. args_standalone = args_instance.copy()
  49. standalone.allocate(args_standalone)
  50. # Get the status of the instance and restart it if it exists
  51. instance_standalone = standalone.exists()
  52. # Remove the instance
  53. if instance_standalone:
  54. standalone.delete()
  55. # Create the instance
  56. standalone.create()
  57. # Used to retrieve configuration information (dbdir, confdir...)
  58. standalone.open()
  59. # clear the tmp directory
  60. standalone.clearTmpDir(__file__)
  61. # Here we have standalone instance up and running
  62. return TopologyStandalone(standalone)
  63. def set_global_pwpolicy(topology, inhistory):
  64. log.info(" +++++ Enable global password policy +++++\n")
  65. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  66. # Enable password policy
  67. try:
  68. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
  69. except ldap.LDAPError as e:
  70. log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
  71. assert False
  72. log.info(" Set global password history on\n")
  73. try:
  74. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordHistory', 'on')])
  75. except ldap.LDAPError as e:
  76. log.error('Failed to set passwordHistory: error ' + e.message['desc'])
  77. assert False
  78. log.info(" Set global passwords in history\n")
  79. try:
  80. count = "%d" % inhistory
  81. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordInHistory', count)])
  82. except ldap.LDAPError as e:
  83. log.error('Failed to set passwordInHistory: error ' + e.message['desc'])
  84. assert False
  85. def set_subtree_pwpolicy(topology):
  86. log.info(" +++++ Enable subtree level password policy +++++\n")
  87. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  88. log.info(" Add the container")
  89. try:
  90. topology.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
  91. 'cn': 'nsPwPolicyContainer'})))
  92. except ldap.LDAPError as e:
  93. log.error('Failed to add subtree container: error ' + e.message['desc'])
  94. assert False
  95. log.info(" Add the password policy subentry {passwordHistory: on, passwordInHistory: 6}")
  96. try:
  97. topology.standalone.add_s(Entry((SUBTREE_PWP, {'objectclass': 'top ldapsubentry passwordpolicy'.split(),
  98. 'cn': SUBTREE_PWPDN,
  99. 'passwordMustChange': 'off',
  100. 'passwordExp': 'off',
  101. 'passwordHistory': 'on',
  102. 'passwordInHistory': '6',
  103. 'passwordMinAge': '0',
  104. 'passwordChange': 'on',
  105. 'passwordStorageScheme': 'clear'})))
  106. except ldap.LDAPError as e:
  107. log.error('Failed to add passwordpolicy: error ' + e.message['desc'])
  108. assert False
  109. log.info(" Add the COS template")
  110. try:
  111. topology.standalone.add_s(Entry((SUBTREE_COS_TMPL, {'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),
  112. 'cn': SUBTREE_PWPDN,
  113. 'cosPriority': '1',
  114. 'cn': SUBTREE_COS_TMPLDN,
  115. 'pwdpolicysubentry': SUBTREE_PWP})))
  116. except ldap.LDAPError as e:
  117. log.error('Failed to add COS template: error ' + e.message['desc'])
  118. assert False
  119. log.info(" Add the COS definition")
  120. try:
  121. topology.standalone.add_s(Entry((SUBTREE_COS_DEF, {'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),
  122. 'cn': SUBTREE_PWPDN,
  123. 'costemplatedn': SUBTREE_COS_TMPL,
  124. 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
  125. except ldap.LDAPError as e:
  126. log.error('Failed to add COS def: error ' + e.message['desc'])
  127. assert False
  128. def check_passwd_inhistory(topology, user, cpw, passwd):
  129. inhistory = 0
  130. log.info(" Bind as {%s,%s}" % (user, cpw))
  131. topology.standalone.simple_bind_s(user, cpw)
  132. try:
  133. topology.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', passwd)])
  134. except ldap.LDAPError as e:
  135. log.info(' The password ' + passwd + ' of user' + USER1_DN + ' in history: error ' + e.message['desc'])
  136. inhistory = 1
  137. return inhistory
  138. def update_passwd(topology, user, passwd, times):
  139. cpw = passwd
  140. loop = 0
  141. while loop < times:
  142. log.info(" Bind as {%s,%s}" % (user, cpw))
  143. topology.standalone.simple_bind_s(user, cpw)
  144. cpw = 'password%d' % loop
  145. try:
  146. topology.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', cpw)])
  147. except ldap.LDAPError as e:
  148. log.fatal('test_ticket48228: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message['desc'])
  149. assert False
  150. loop += 1
  151. # checking the first password, which is supposed to be in history
  152. inhistory = check_passwd_inhistory(topology, user, cpw, passwd)
  153. assert inhistory == 1
  154. def test_ticket48228_test_global_policy(topology):
  155. """
  156. Check global password policy
  157. """
  158. log.info(' Set inhistory = 6')
  159. set_global_pwpolicy(topology, 6)
  160. log.info(' Bind as directory manager')
  161. log.info("Bind as %s" % DN_DM)
  162. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  163. log.info(' Add an entry' + USER1_DN)
  164. try:
  165. topology.standalone.add_s(Entry((USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  166. 'sn': '1',
  167. 'cn': 'user 1',
  168. 'uid': 'user1',
  169. 'givenname': 'user',
  170. 'mail': '[email protected]',
  171. 'userpassword': 'password'})))
  172. except ldap.LDAPError as e:
  173. log.fatal('test_ticket48228: Failed to add user' + USER1_DN + ': error ' + e.message['desc'])
  174. assert False
  175. log.info(' Update the password of ' + USER1_DN + ' 6 times')
  176. update_passwd(topology, USER1_DN, 'password', 6)
  177. log.info(' Set inhistory = 4')
  178. set_global_pwpolicy(topology, 4)
  179. log.info(' checking the first password, which is supposed NOT to be in history any more')
  180. cpw = 'password%d' % 5
  181. tpw = 'password'
  182. inhistory = check_passwd_inhistory(topology, USER1_DN, cpw, tpw)
  183. assert inhistory == 0
  184. log.info(' checking the second password, which is supposed NOT to be in history any more')
  185. cpw = tpw
  186. tpw = 'password%d' % 0
  187. inhistory = check_passwd_inhistory(topology, USER1_DN, cpw, tpw)
  188. assert inhistory == 0
  189. log.info(' checking the second password, which is supposed NOT to be in history any more')
  190. cpw = tpw
  191. tpw = 'password%d' % 1
  192. inhistory = check_passwd_inhistory(topology, USER1_DN, cpw, tpw)
  193. assert inhistory == 0
  194. log.info(' checking the third password, which is supposed to be in history')
  195. cpw = tpw
  196. tpw = 'password%d' % 2
  197. inhistory = check_passwd_inhistory(topology, USER1_DN, cpw, tpw)
  198. assert inhistory == 1
  199. log.info("Global policy was successfully verified.")
  200. def test_ticket48228_test_subtree_policy(topology):
  201. """
  202. Check subtree level password policy
  203. """
  204. log.info(' Set inhistory = 6')
  205. set_subtree_pwpolicy(topology)
  206. log.info(' Bind as directory manager')
  207. log.info("Bind as %s" % DN_DM)
  208. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  209. log.info(' Add an entry' + USER2_DN)
  210. try:
  211. topology.standalone.add_s(Entry((USER2_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  212. 'sn': '2',
  213. 'cn': 'user 2',
  214. 'uid': 'user2',
  215. 'givenname': 'user',
  216. 'mail': '[email protected]',
  217. 'userpassword': 'password'})))
  218. except ldap.LDAPError as e:
  219. log.fatal('test_ticket48228: Failed to add user' + USER2_DN + ': error ' + e.message['desc'])
  220. assert False
  221. log.info(' Update the password of ' + USER2_DN + ' 6 times')
  222. update_passwd(topology, USER2_DN, 'password', 6)
  223. log.info(' Set inhistory = 4')
  224. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  225. try:
  226. topology.standalone.modify_s(SUBTREE_PWP, [(ldap.MOD_REPLACE, 'passwordInHistory', '4')])
  227. except ldap.LDAPError as e:
  228. log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
  229. assert False
  230. log.info(' checking the first password, which is supposed NOT to be in history any more')
  231. cpw = 'password%d' % 5
  232. tpw = 'password'
  233. inhistory = check_passwd_inhistory(topology, USER2_DN, cpw, tpw)
  234. assert inhistory == 0
  235. log.info(' checking the second password, which is supposed NOT to be in history any more')
  236. cpw = tpw
  237. tpw = 'password%d' % 0
  238. inhistory = check_passwd_inhistory(topology, USER2_DN, cpw, tpw)
  239. assert inhistory == 0
  240. log.info(' checking the second password, which is supposed NOT to be in history any more')
  241. cpw = tpw
  242. tpw = 'password%d' % 1
  243. inhistory = check_passwd_inhistory(topology, USER2_DN, cpw, tpw)
  244. assert inhistory == 0
  245. log.info(' checking the third password, which is supposed to be in history')
  246. cpw = tpw
  247. tpw = 'password%d' % 2
  248. inhistory = check_passwd_inhistory(topology, USER2_DN, cpw, tpw)
  249. assert inhistory == 1
  250. log.info("Subtree level policy was successfully verified.")
  251. def test_ticket48228_final(topology):
  252. topology.standalone.delete()
  253. log.info('Testcase PASSED')
  254. def run_isolated():
  255. '''
  256. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  257. To run isolated without py.test, you need to
  258. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  259. - set the installation prefix
  260. - run this program
  261. '''
  262. global installation_prefix
  263. installation_prefix = None
  264. topo = topology(True)
  265. log.info('Testing Ticket 48228 - wrong password check if passwordInHistory is decreased')
  266. test_ticket48228_test_global_policy(topo)
  267. test_ticket48228_test_subtree_policy(topo)
  268. test_ticket48228_final(topo)
  269. if __name__ == '__main__':
  270. run_isolated()