ticket548_test.py 16 KB

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