ticket548_test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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\2Cdc\3Dexample\2Cdc\3Dcom,' + SUBTREE_CONTAINER
  27. SUBTREE_COS_TMPLDN = 'cn=nsPwTemplateEntry,' + DEFAULT_SUFFIX
  28. SUBTREE_COS_TMPL = 'cn=cn\3DnsPwTemplateEntry\2Cdc\3Dexample\2Cdc\3Dcom,' + 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 set_global_pwpolicy(topology):
  64. log.info(" +++++ Enable global password policy +++++\n")
  65. # Enable password policy
  66. try:
  67. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
  68. except ldap.LDAPError as e:
  69. log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
  70. assert False
  71. log.info(" Set global password Min Age -- 1 day\n")
  72. try:
  73. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', '86400')])
  74. except ldap.LDAPError as e:
  75. log.error('Failed to set passwordMinAge: error ' + e.message['desc'])
  76. assert False
  77. log.info(" Set global password Max Age -- 10 days\n")
  78. try:
  79. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', '864000')])
  80. except ldap.LDAPError as e:
  81. log.error('Failed to set passwordMaxAge: error ' + e.message['desc'])
  82. assert False
  83. log.info(" Set global password Warning -- 3 days\n")
  84. try:
  85. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', '259200')])
  86. except ldap.LDAPError as e:
  87. log.error('Failed to set passwordWarning: error ' + e.message['desc'])
  88. assert False
  89. def set_subtree_pwpolicy(topology):
  90. log.info(" +++++ Enable subtree level password policy +++++\n")
  91. log.info(" Add the container")
  92. try:
  93. topology.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
  94. 'cn': 'nsPwPolicyContainer'})))
  95. except ldap.LDAPError as e:
  96. log.error('Failed to add subtree container: error ' + e.message['desc'])
  97. assert False
  98. log.info(" Add the password policy subentry {passwordMustChange: on, passwordMinAge: 2, passwordMaxAge: 20, passwordWarning: 6}")
  99. try:
  100. topology.standalone.add_s(Entry((SUBTREE_PWP, {'objectclass': 'top ldapsubentry passwordpolicy'.split(),
  101. 'cn': SUBTREE_PWPDN,
  102. 'passwordMustChange': 'on',
  103. 'passwordExp': 'on',
  104. 'passwordMinAge': '172800',
  105. 'passwordMaxAge': '1728000',
  106. 'passwordWarning': '518400',
  107. 'passwordChange': 'on',
  108. 'passwordStorageScheme': 'clear'})))
  109. except ldap.LDAPError as e:
  110. log.error('Failed to add passwordpolicy: error ' + e.message['desc'])
  111. assert False
  112. log.info(" Add the COS template")
  113. try:
  114. topology.standalone.add_s(Entry((SUBTREE_COS_TMPL, {'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),
  115. 'cn': SUBTREE_PWPDN,
  116. 'cosPriority': '1',
  117. 'cn': SUBTREE_COS_TMPLDN,
  118. 'pwdpolicysubentry': SUBTREE_PWP})))
  119. except ldap.LDAPError as e:
  120. log.error('Failed to add COS template: error ' + e.message['desc'])
  121. assert False
  122. log.info(" Add the COS definition")
  123. try:
  124. topology.standalone.add_s(Entry((SUBTREE_COS_DEF, {'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),
  125. 'cn': SUBTREE_PWPDN,
  126. 'costemplatedn': SUBTREE_COS_TMPL,
  127. 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
  128. except ldap.LDAPError as e:
  129. log.error('Failed to add COS def: error ' + e.message['desc'])
  130. assert False
  131. time.sleep(1)
  132. def update_passwd(topology, user, passwd, newpasswd):
  133. log.info(" Bind as {%s,%s}" % (user, passwd))
  134. topology.standalone.simple_bind_s(user, passwd)
  135. try:
  136. topology.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd)])
  137. except ldap.LDAPError as e:
  138. log.fatal('test_ticket548: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message['desc'])
  139. assert False
  140. time.sleep(1)
  141. def check_shadow_attr_value(entry, attr_type, expected, dn):
  142. if entry.hasAttr(attr_type):
  143. actual = entry.getValue(attr_type)
  144. if int(actual) == expected:
  145. log.info('%s of entry %s has expected value %s' % (attr_type, dn, actual))
  146. assert True
  147. else:
  148. log.fatal('%s %s of entry %s does not have expected value %s' % (attr_type, actual, dn, expected))
  149. assert False
  150. else:
  151. log.fatal('entry %s does not have %s attr' % (dn, attr_type))
  152. assert False
  153. def test_ticket548_test_with_no_policy(topology):
  154. """
  155. Check shadowAccount under no password policy
  156. """
  157. log.info("Case 1. No password policy")
  158. log.info("Bind as %s" % DN_DM)
  159. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  160. log.info('Add an entry' + USER1_DN)
  161. try:
  162. topology.standalone.add_s(Entry((USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  163. 'sn': '1',
  164. 'cn': 'user 1',
  165. 'uid': 'user1',
  166. 'givenname': 'user',
  167. 'mail': '[email protected]',
  168. 'userpassword': USER_PW})))
  169. except ldap.LDAPError as e:
  170. log.fatal('test_ticket548: Failed to add user' + USER1_DN + ': error ' + e.message['desc'])
  171. assert False
  172. edate = int(time.time() / (60 * 60 * 24))
  173. log.info('Search entry %s' % USER1_DN)
  174. log.info("Bind as %s" % USER1_DN)
  175. topology.standalone.simple_bind_s(USER1_DN, USER_PW)
  176. entry = topology.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)", ['shadowLastChange'])
  177. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
  178. log.info("Check shadowAccount with no policy was successfully verified.")
  179. def test_ticket548_test_global_policy(topology):
  180. """
  181. Check shadowAccount with global password policy
  182. """
  183. log.info("Case 2. Check shadowAccount with global password policy")
  184. log.info("Bind as %s" % DN_DM)
  185. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  186. set_global_pwpolicy(topology)
  187. log.info('Add an entry' + USER2_DN)
  188. try:
  189. topology.standalone.add_s(Entry((USER2_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  190. 'sn': '2',
  191. 'cn': 'user 2',
  192. 'uid': 'user2',
  193. 'givenname': 'user',
  194. 'mail': '[email protected]',
  195. 'userpassword': USER_PW})))
  196. except ldap.LDAPError as e:
  197. log.fatal('test_ticket548: Failed to add user' + USER2_DN + ': error ' + e.message['desc'])
  198. assert False
  199. log.info("Bind as %s" % USER2_DN)
  200. topology.standalone.simple_bind_s(USER2_DN, USER_PW)
  201. edate = int(time.time() / (60 * 60 * 24))
  202. log.info('Search entry %s' % USER2_DN)
  203. entry = topology.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  204. check_shadow_attr_value(entry, 'shadowLastChange', edate, USER2_DN)
  205. # passwordMinAge -- 1 day
  206. check_shadow_attr_value(entry, 'shadowMin', 1, USER2_DN)
  207. # passwordMaxAge -- 10 days
  208. check_shadow_attr_value(entry, 'shadowMax', 10, USER2_DN)
  209. # passwordWarning -- 3 days
  210. check_shadow_attr_value(entry, 'shadowWarning', 3, USER2_DN)
  211. log.info("Check shadowAccount with global policy was successfully verified.")
  212. def test_ticket548_test_subtree_policy(topology):
  213. """
  214. Check shadowAccount with subtree level password policy
  215. """
  216. log.info("Case 3. Check shadowAccount with subtree level password policy")
  217. log.info("Bind as %s" % DN_DM)
  218. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  219. set_subtree_pwpolicy(topology)
  220. log.info('Add an entry' + USER3_DN)
  221. try:
  222. topology.standalone.add_s(Entry((USER3_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
  223. 'sn': '3',
  224. 'cn': 'user 3',
  225. 'uid': 'user3',
  226. 'givenname': 'user',
  227. 'mail': '[email protected]',
  228. 'userpassword': USER_PW})))
  229. except ldap.LDAPError as e:
  230. log.fatal('test_ticket548: Failed to add user' + USER3_DN + ': error ' + e.message['desc'])
  231. assert False
  232. log.info('Search entry %s' % USER3_DN)
  233. entry0 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  234. log.info('Expecting shadowLastChange 0 since passwordMustChange is on')
  235. check_shadow_attr_value(entry0, 'shadowLastChange', 0, USER3_DN)
  236. # passwordMinAge -- 2 day
  237. check_shadow_attr_value(entry0, 'shadowMin', 2, USER3_DN)
  238. # passwordMaxAge -- 20 days
  239. check_shadow_attr_value(entry0, 'shadowMax', 20, USER3_DN)
  240. # passwordWarning -- 6 days
  241. check_shadow_attr_value(entry0, 'shadowWarning', 6, USER3_DN)
  242. log.info("Bind as %s" % USER3_DN)
  243. topology.standalone.simple_bind_s(USER3_DN, USER_PW)
  244. log.info('Search entry %s' % USER3_DN)
  245. try:
  246. entry1 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  247. except ldap.UNWILLING_TO_PERFORM:
  248. log.info('test_ticket548: Search by' + USER3_DN + ' failed by UNWILLING_TO_PERFORM as expected')
  249. except ldap.LDAPError as e:
  250. log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
  251. assert False
  252. log.info("Bind as %s and updating the password with a new one" % USER3_DN)
  253. topology.standalone.simple_bind_s(USER3_DN, USER_PW)
  254. newpasswd = USER_PW + '0'
  255. update_passwd(topology, USER3_DN, USER_PW, newpasswd)
  256. log.info("Re-bind as %s with new password" % USER3_DN)
  257. topology.standalone.simple_bind_s(USER3_DN, newpasswd)
  258. try:
  259. entry2 = topology.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  260. except ldap.LDAPError as e:
  261. log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
  262. assert False
  263. edate = int(time.time() / (60 * 60 * 24))
  264. log.info('Expecting shadowLastChange %d once userPassword is updated', edate)
  265. check_shadow_attr_value(entry2, 'shadowLastChange', edate, USER3_DN)
  266. log.info("Check shadowAccount with subtree level policy was successfully verified.")
  267. if __name__ == '__main__':
  268. # Run isolated
  269. # -s for DEBUG mode
  270. CURRENT_FILE = os.path.realpath(__file__)
  271. pytest.main("-s %s" % CURRENT_FILE)