ticket548_test.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. from lib389.utils import *
  21. log = logging.getLogger(__name__)
  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\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + SUBTREE_CONTAINER
  26. SUBTREE_COS_TMPLDN = 'cn=nsPwTemplateEntry,' + DEFAULT_SUFFIX
  27. SUBTREE_COS_TMPL = 'cn=cn\3DnsPwTemplateEntry\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + 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. USER3_DN = 'uid=user3,' + DEFAULT_SUFFIX
  32. USER_PW = 'password'
  33. class TopologyStandalone(object):
  34. def __init__(self, standalone):
  35. standalone.open()
  36. self.standalone = standalone
  37. @pytest.fixture(scope="module")
  38. def topology(request):
  39. # Creating standalone instance ...
  40. standalone = DirSrv(verbose=False)
  41. args_instance[SER_HOST] = HOST_STANDALONE
  42. args_instance[SER_PORT] = PORT_STANDALONE
  43. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  44. args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
  45. args_standalone = args_instance.copy()
  46. standalone.allocate(args_standalone)
  47. instance_standalone = standalone.exists()
  48. if instance_standalone:
  49. standalone.delete()
  50. standalone.create()
  51. standalone.open()
  52. # Delete each instance in the end
  53. def fin():
  54. standalone.delete()
  55. request.addfinalizer(fin)
  56. return TopologyStandalone(standalone)
  57. def days_to_secs(days):
  58. # Value of 60 * 60 * 24
  59. return days * 86400
  60. # Values are in days
  61. def set_global_pwpolicy(topology, min_=1, max_=10, warn=3):
  62. log.info(" +++++ Enable global password policy +++++\n")
  63. # Enable password policy
  64. try:
  65. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
  66. except ldap.LDAPError as e:
  67. log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
  68. assert False
  69. # Convert our values to seconds
  70. min_secs = days_to_secs(min_)
  71. max_secs = days_to_secs(max_)
  72. warn_secs = days_to_secs(warn)
  73. log.info(" Set global password Min Age -- %s day\n"% min_)
  74. try:
  75. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', '%s' % min_secs)])
  76. except ldap.LDAPError as e:
  77. log.error('Failed to set passwordMinAge: error ' + e.message['desc'])
  78. assert False
  79. log.info(" Set global password Max Age -- %s days\n" % max_)
  80. try:
  81. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', '%s' % max_secs)])
  82. except ldap.LDAPError as e:
  83. log.error('Failed to set passwordMaxAge: error ' + e.message['desc'])
  84. assert False
  85. log.info(" Set global password Warning -- %s days\n" % warn)
  86. try:
  87. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', '%s' % warn_secs)])
  88. except ldap.LDAPError as e:
  89. log.error('Failed to set passwordWarning: error ' + e.message['desc'])
  90. assert False
  91. def set_subtree_pwpolicy(topology, min_=2, max_=20, warn=6):
  92. log.info(" +++++ Enable subtree level password policy +++++\n")
  93. # Convert our values to seconds
  94. min_secs = days_to_secs(min_)
  95. max_secs = days_to_secs(max_)
  96. warn_secs = days_to_secs(warn)
  97. log.info(" Add the container")
  98. try:
  99. topology.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
  100. 'cn': 'nsPwPolicyContainer'})))
  101. except ldap.LDAPError as e:
  102. log.error('Failed to add subtree container: error ' + e.message['desc'])
  103. #assert False
  104. try:
  105. # Purge the old policy
  106. topology.standalone.delete_s(SUBTREE_PWP)
  107. except:
  108. pass
  109. log.info(" Add the password policy subentry {passwordMustChange: on, passwordMinAge: %s, passwordMaxAge: %s, passwordWarning: %s}" % (min_, max_, warn))
  110. try:
  111. topology.standalone.add_s(Entry((SUBTREE_PWP, {'objectclass': 'top ldapsubentry passwordpolicy'.split(),
  112. 'cn': SUBTREE_PWPDN,
  113. 'passwordMustChange': 'on',
  114. 'passwordExp': 'on',
  115. 'passwordMinAge': '%s' % min_secs,
  116. 'passwordMaxAge': '%s' % max_secs,
  117. 'passwordWarning': '%s' % warn_secs,
  118. 'passwordChange': 'on',
  119. 'passwordStorageScheme': 'clear'})))
  120. except ldap.LDAPError as e:
  121. log.error('Failed to add passwordpolicy: error ' + e.message['desc'])
  122. assert False
  123. log.info(" Add the COS template")
  124. try:
  125. topology.standalone.add_s(Entry((SUBTREE_COS_TMPL, {'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),
  126. 'cn': SUBTREE_PWPDN,
  127. 'cosPriority': '1',
  128. 'cn': SUBTREE_COS_TMPLDN,
  129. 'pwdpolicysubentry': SUBTREE_PWP})))
  130. except ldap.LDAPError as e:
  131. log.error('Failed to add COS template: error ' + e.message['desc'])
  132. #assert False
  133. log.info(" Add the COS definition")
  134. try:
  135. topology.standalone.add_s(Entry((SUBTREE_COS_DEF, {'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),
  136. 'cn': SUBTREE_PWPDN,
  137. 'costemplatedn': SUBTREE_COS_TMPL,
  138. 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
  139. except ldap.LDAPError as e:
  140. log.error('Failed to add COS def: error ' + e.message['desc'])
  141. #assert False
  142. time.sleep(1)
  143. def update_passwd(topology, user, passwd, newpasswd):
  144. log.info(" Bind as {%s,%s}" % (user, passwd))
  145. topology.standalone.simple_bind_s(user, passwd)
  146. try:
  147. topology.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd)])
  148. except ldap.LDAPError as e:
  149. log.fatal('test_ticket548: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message['desc'])
  150. assert False
  151. time.sleep(1)
  152. def check_shadow_attr_value(entry, attr_type, expected, dn):
  153. if entry.hasAttr(attr_type):
  154. actual = entry.getValue(attr_type)
  155. if int(actual) == expected:
  156. log.info('%s of entry %s has expected value %s' % (attr_type, dn, actual))
  157. assert True
  158. else:
  159. log.fatal('%s %s of entry %s does not have expected value %s' % (attr_type, actual, dn, expected))
  160. assert False
  161. else:
  162. log.fatal('entry %s does not have %s attr' % (dn, attr_type))
  163. assert False
  164. def test_ticket548_test_with_no_policy(topology):
  165. """
  166. Check shadowAccount under no password policy
  167. """
  168. log.info("Case 1. No password policy")
  169. log.info("Bind as %s" % DN_DM)
  170. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  171. log.info('Add an entry' + USER1_DN)
  172. try:
  173. topology.standalone.add_s(Entry((USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  174. 'sn': '1',
  175. 'cn': 'user 1',
  176. 'uid': 'user1',
  177. 'givenname': 'user',
  178. 'mail': 'user1@' + DEFAULT_SUFFIX,
  179. 'userpassword': USER_PW})))
  180. except ldap.LDAPError as e:
  181. log.fatal('test_ticket548: Failed to add user' + USER1_DN + ': error ' + e.message['desc'])
  182. assert False
  183. edate = int(time.time() / (60 * 60 * 24))
  184. log.info('Search entry %s' % USER1_DN)
  185. log.info("Bind as %s" % USER1_DN)
  186. topology.standalone.simple_bind_s(USER1_DN, USER_PW)
  187. entry = topology.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)", ['shadowLastChange'])
  188. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
  189. log.info("Check shadowAccount with no policy was successfully verified.")
  190. def test_ticket548_test_global_policy(topology):
  191. """
  192. Check shadowAccount with global password policy
  193. """
  194. log.info("Case 2. Check shadowAccount with global password policy")
  195. log.info("Bind as %s" % DN_DM)
  196. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  197. set_global_pwpolicy(topology)
  198. log.info('Add an entry' + USER2_DN)
  199. try:
  200. topology.standalone.add_s(Entry((USER2_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  201. 'sn': '2',
  202. 'cn': 'user 2',
  203. 'uid': 'user2',
  204. 'givenname': 'user',
  205. 'mail': 'user2@' + DEFAULT_SUFFIX,
  206. 'userpassword': USER_PW})))
  207. except ldap.LDAPError as e:
  208. log.fatal('test_ticket548: Failed to add user' + USER2_DN + ': error ' + e.message['desc'])
  209. assert False
  210. edate = int(time.time() / (60 * 60 * 24))
  211. log.info("Bind as %s" % USER1_DN)
  212. topology.standalone.simple_bind_s(USER1_DN, USER_PW)
  213. log.info('Search entry %s' % USER1_DN)
  214. entry = topology.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  215. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
  216. # passwordMinAge -- 1 day
  217. check_shadow_attr_value(entry, 'shadowMin', 1, USER1_DN)
  218. # passwordMaxAge -- 10 days
  219. check_shadow_attr_value(entry, 'shadowMax', 10, USER1_DN)
  220. # passwordWarning -- 3 days
  221. check_shadow_attr_value(entry, 'shadowWarning', 3, USER1_DN)
  222. log.info("Bind as %s" % USER2_DN)
  223. topology.standalone.simple_bind_s(USER2_DN, USER_PW)
  224. log.info('Search entry %s' % USER2_DN)
  225. entry = topology.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  226. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER2_DN)
  227. # passwordMinAge -- 1 day
  228. check_shadow_attr_value(entry, 'shadowMin', 1, USER2_DN)
  229. # passwordMaxAge -- 10 days
  230. check_shadow_attr_value(entry, 'shadowMax', 10, USER2_DN)
  231. # passwordWarning -- 3 days
  232. check_shadow_attr_value(entry, 'shadowWarning', 3, USER2_DN)
  233. # Bind as DM again, change policy
  234. log.info("Bind as %s" % DN_DM)
  235. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  236. set_global_pwpolicy(topology, 3, 30, 9)
  237. # change the user password, then check again.
  238. log.info("Bind as %s" % USER2_DN)
  239. topology.standalone.simple_bind_s(USER2_DN, USER_PW)
  240. newpasswd = USER_PW + '2'
  241. update_passwd(topology, USER2_DN, USER_PW, newpasswd)
  242. log.info("Re-bind as %s with new password" % USER2_DN)
  243. topology.standalone.simple_bind_s(USER2_DN, newpasswd)
  244. ## This tests if we update the shadow values on password change.
  245. log.info('Search entry %s' % USER2_DN)
  246. entry = topology.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  247. # passwordMinAge -- 1 day
  248. check_shadow_attr_value(entry, 'shadowMin', 3, USER2_DN)
  249. # passwordMaxAge -- 10 days
  250. check_shadow_attr_value(entry, 'shadowMax', 30, USER2_DN)
  251. # passwordWarning -- 3 days
  252. check_shadow_attr_value(entry, 'shadowWarning', 9, USER2_DN)
  253. log.info("Check shadowAccount with global policy was successfully verified.")
  254. def test_ticket548_test_subtree_policy(topology):
  255. """
  256. Check shadowAccount with subtree level password policy
  257. """
  258. log.info("Case 3. Check shadowAccount with subtree level password policy")
  259. log.info("Bind as %s" % DN_DM)
  260. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  261. # Check the global policy values
  262. set_subtree_pwpolicy(topology, 2, 20, 6)
  263. log.info('Add an entry' + USER3_DN)
  264. try:
  265. topology.standalone.add_s(Entry((USER3_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  266. 'sn': '3',
  267. 'cn': 'user 3',
  268. 'uid': 'user3',
  269. 'givenname': 'user',
  270. 'mail': 'user3@' + DEFAULT_SUFFIX,
  271. 'userpassword': USER_PW})))
  272. except ldap.LDAPError as e:
  273. log.fatal('test_ticket548: Failed to add user' + USER3_DN + ': error ' + e.message['desc'])
  274. assert False
  275. log.info('Search entry %s' % USER3_DN)
  276. entry0 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  277. log.info('Expecting shadowLastChange 0 since passwordMustChange is on')
  278. check_shadow_attr_value(entry0, 'shadowLastChange', 0, USER3_DN)
  279. # passwordMinAge -- 2 day
  280. check_shadow_attr_value(entry0, 'shadowMin', 2, USER3_DN)
  281. # passwordMaxAge -- 20 days
  282. check_shadow_attr_value(entry0, 'shadowMax', 20, USER3_DN)
  283. # passwordWarning -- 6 days
  284. check_shadow_attr_value(entry0, 'shadowWarning', 6, USER3_DN)
  285. log.info("Bind as %s" % USER3_DN)
  286. topology.standalone.simple_bind_s(USER3_DN, USER_PW)
  287. log.info('Search entry %s' % USER3_DN)
  288. try:
  289. entry1 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  290. except ldap.UNWILLING_TO_PERFORM:
  291. log.info('test_ticket548: Search by' + USER3_DN + ' failed by UNWILLING_TO_PERFORM as expected')
  292. except ldap.LDAPError as e:
  293. log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
  294. assert False
  295. log.info("Bind as %s and updating the password with a new one" % USER3_DN)
  296. topology.standalone.simple_bind_s(USER3_DN, USER_PW)
  297. # Bind as DM again, change policy
  298. log.info("Bind as %s" % DN_DM)
  299. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  300. set_subtree_pwpolicy(topology, 4, 40, 12)
  301. newpasswd = USER_PW + '0'
  302. update_passwd(topology, USER3_DN, USER_PW, newpasswd)
  303. log.info("Re-bind as %s with new password" % USER3_DN)
  304. topology.standalone.simple_bind_s(USER3_DN, newpasswd)
  305. try:
  306. entry2 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  307. except ldap.LDAPError as e:
  308. log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
  309. assert False
  310. edate = int(time.time() / (60 * 60 * 24))
  311. log.info('Expecting shadowLastChange %d once userPassword is updated', edate)
  312. check_shadow_attr_value(entry2, 'shadowLastChange', edate, USER3_DN)
  313. log.info('Search entry %s' % USER3_DN)
  314. entry = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  315. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER3_DN)
  316. # passwordMinAge -- 1 day
  317. check_shadow_attr_value(entry, 'shadowMin', 4, USER3_DN)
  318. # passwordMaxAge -- 10 days
  319. check_shadow_attr_value(entry, 'shadowMax', 40, USER3_DN)
  320. # passwordWarning -- 3 days
  321. check_shadow_attr_value(entry, 'shadowWarning', 12, USER3_DN)
  322. log.info("Check shadowAccount with subtree level policy was successfully verified.")
  323. if __name__ == '__main__':
  324. # Run isolated
  325. # -s for DEBUG mode
  326. CURRENT_FILE = os.path.realpath(__file__)
  327. pytest.main("-s %s" % CURRENT_FILE)