ticket142_test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. import shutil
  16. from lib389 import DirSrv, Entry, tools
  17. from lib389 import DirSrvTools
  18. from lib389.tools import DirSrvTools
  19. from lib389._constants import *
  20. from lib389.properties import *
  21. log = logging.getLogger(__name__)
  22. installation_prefix = None
  23. CONFIG_DN = 'cn=config'
  24. OU_PEOPLE = 'ou=People,' + DEFAULT_SUFFIX
  25. PWP_CONTAINER = 'nsPwPolicyContainer'
  26. PWP_CONTAINER_DN = 'cn=' + PWP_CONTAINER + ',' + OU_PEOPLE
  27. PWP_ENTRY_DN = 'cn=nsPwPolicyEntry,' + OU_PEOPLE
  28. PWP_TEMPLATE_ENTRY_DN = 'cn=nsPwTemplateEntry,' + OU_PEOPLE
  29. ATTR_INHERIT_GLOBAL = 'nsslapd-pwpolicy-inherit-global'
  30. BN = 'uid=buser,' + 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. def fin():
  60. standalone.delete()
  61. request.addfinalizer(fin)
  62. # Here we have standalone instance up and running
  63. return TopologyStandalone(standalone)
  64. def _header(topology, label):
  65. topology.standalone.log.info("###############################################")
  66. topology.standalone.log.info("####### %s" % label)
  67. topology.standalone.log.info("###############################################")
  68. def check_attr_val(topology, dn, attr, expected):
  69. try:
  70. centry = topology.standalone.search_s(dn, ldap.SCOPE_BASE, 'cn=*')
  71. if centry:
  72. val = centry[0].getValue(attr)
  73. if val == expected:
  74. log.info('Default value of %s is %s' % (attr, expected))
  75. else:
  76. log.info('Default value of %s is not %s, but %s' % (attr, expected, val))
  77. assert False
  78. else:
  79. log.fatal('Failed to get %s' % dn)
  80. assert False
  81. except ldap.LDAPError as e:
  82. log.fatal('Failed to search ' + dn + ': ' + e.message['desc'])
  83. assert False
  84. def _142_init(topology):
  85. """
  86. Set global password policy.
  87. Then, set fine-grained subtree level password policy to ou=People with no password syntax.
  88. Note: do not touch nsslapd-pwpolicy-inherit-global -- off by default
  89. Also, adding an ordinary bind user.
  90. """
  91. _header(topology, 'Testing Ticket 142 - Default password syntax settings do not work with fine-grained policies')
  92. log.info("Setting global password policy with password syntax.")
  93. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  94. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', 'on')])
  95. log.info("Setting fine-grained password policy.")
  96. topology.standalone.add_s(Entry((PWP_CONTAINER_DN, {
  97. 'objectclass': "top nsContainer".split()})))
  98. topology.standalone.add_s(Entry(('cn="%s",%s' % (PWP_ENTRY_DN, PWP_CONTAINER_DN), {
  99. 'objectclass': "top ldapsubentry passwordpolicy".split()})))
  100. topology.standalone.add_s(Entry(('cn="%s",%s' % (PWP_TEMPLATE_ENTRY_DN, PWP_CONTAINER_DN), {
  101. 'objectclass': "top ldapsubentry costemplate".split(),
  102. 'pwdpolicysubentry': 'cn="%s",%s' % (PWP_ENTRY_DN, PWP_CONTAINER_DN)})))
  103. topology.standalone.add_s(Entry(('cn=nsPwPolicy_CoS,%s' % OU_PEOPLE, {
  104. 'objectclass': "top ldapsubentry cosSuperDefinition cosPointerDefinition".split(),
  105. 'cosTemplateDn': 'cn="%s",%s' % (PWP_TEMPLATE_ENTRY_DN, PWP_CONTAINER_DN),
  106. 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
  107. log.info(" with the default settings.")
  108. topology.standalone.modify_s('cn="%s",%s' % (PWP_ENTRY_DN, PWP_CONTAINER_DN),
  109. [(ldap.MOD_REPLACE, 'passwordMustChange', 'off'),
  110. (ldap.MOD_REPLACE, 'passwordExp', 'off'),
  111. (ldap.MOD_REPLACE, 'passwordMinAge', '0'),
  112. (ldap.MOD_REPLACE, 'passwordChange', 'off'),
  113. (ldap.MOD_REPLACE, 'passwordStorageScheme', 'ssha')])
  114. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'off')
  115. check_attr_val(topology, CONFIG_DN, 'passwordCheckSyntax', 'off')
  116. log.info('Adding a bind user.')
  117. topology.standalone.add_s(Entry((BN,
  118. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  119. 'cn': 'bind user',
  120. 'sn': 'bind user',
  121. 'userPassword': PASSWORD})))
  122. log.info('Adding an aci for the bind user.')
  123. topology.standalone.modify_s(OU_PEOPLE,
  124. [(ldap.MOD_ADD,
  125. 'aci',
  126. '(targetattr="*")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///%s";)' % BN)])
  127. def _142_run_0(topology):
  128. """
  129. Make sure an entry added to ou=people has no password syntax restrictions.
  130. """
  131. _header(topology, 'Case 0 - Make sure an entry added to ou=people has no password syntax restrictions.')
  132. topology.standalone.simple_bind_s(BN, PASSWORD)
  133. try:
  134. topology.standalone.add_s(Entry(('cn=test0,%s' % OU_PEOPLE,
  135. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  136. 'cn': 'test0',
  137. 'sn': 'test0',
  138. 'userPassword': 'short'})))
  139. except ldap.LDAPError as e:
  140. log.fatal('Failed to add cn=test0 with userPassword: short: ' + e.message['desc'])
  141. assert False
  142. log.info('PASSED')
  143. def _142_run_1(topology):
  144. """
  145. Set 'nsslapd-pwpolicy-inherit-global: on'
  146. But passwordCheckSyntax is still off.
  147. Make sure an entry added to ou=people has the global password syntax restrictions.
  148. """
  149. _header(topology, 'Case 1 - Make sure an entry added to ou=people has no password syntax restrictions.')
  150. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  151. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, ATTR_INHERIT_GLOBAL, 'on')])
  152. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'on')
  153. check_attr_val(topology, CONFIG_DN, 'passwordCheckSyntax', 'off')
  154. topology.standalone.simple_bind_s(BN, PASSWORD)
  155. try:
  156. topology.standalone.add_s(Entry(('cn=test1,%s' % OU_PEOPLE,
  157. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  158. 'cn': 'test1',
  159. 'sn': 'test1',
  160. 'userPassword': 'short'})))
  161. except ldap.LDAPError as e:
  162. log.fatal('Failed to add cn=test1 with userPassword: short: ' + e.message['desc'])
  163. assert False
  164. log.info('PASSED')
  165. def _142_run_2(topology):
  166. """
  167. Set 'passwordCheckSyntax: on'
  168. Set 'passwordMinLength: 9' for testing
  169. Make sure an entry added to ou=people has the global password syntax restrictions.
  170. """
  171. _header(topology, 'Case 2 - Make sure an entry added to ou=people has the global password syntax restrictions.')
  172. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  173. topology.standalone.modify_s(CONFIG_DN,
  174. [(ldap.MOD_REPLACE, 'passwordCheckSyntax', 'on'),
  175. (ldap.MOD_REPLACE, 'passwordMinLength', '9')])
  176. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'on')
  177. check_attr_val(topology, CONFIG_DN, 'passwordCheckSyntax', 'on')
  178. topology.standalone.simple_bind_s(BN, PASSWORD)
  179. failed_as_expected = False
  180. try:
  181. topology.standalone.add_s(Entry(('cn=test2,%s' % OU_PEOPLE,
  182. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  183. 'cn': 'test2',
  184. 'sn': 'test2',
  185. 'userPassword': 'Abcd2345'})))
  186. except ldap.LDAPError as e:
  187. log.info('Adding cn=test2 with "userPassword: Abcd2345" was expectedly rejected: ' + e.message['desc'])
  188. failed_as_expected = True
  189. if not failed_as_expected:
  190. log.fatal('Adding cn=test2 with "userPassword: Abcd2345" was unexpectedly successful despite of short password.')
  191. assert False
  192. try:
  193. topology.standalone.add_s(Entry(('cn=test2,%s' % OU_PEOPLE,
  194. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  195. 'cn': 'test2',
  196. 'sn': 'test2',
  197. 'userPassword': 'Abcd23456'})))
  198. except ldap.LDAPError as e:
  199. log.fatal('Adding cn=test2 with "userPassword: Abcd23456" failed: ' + e.message['desc'])
  200. assert False
  201. log.info('PASSED')
  202. def _142_run_3(topology):
  203. """
  204. Set 'passwordCheckSyntax: on'
  205. Set 'nsslapd-pwpolicy-inherit-global: off'
  206. Make sure an entry added to ou=people has no syntax restrictions.
  207. """
  208. _header(topology, 'Case 3 - Make sure an entry added to ou=people has no password syntax restrictions.')
  209. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  210. topology.standalone.modify_s(CONFIG_DN,
  211. [(ldap.MOD_REPLACE, ATTR_INHERIT_GLOBAL, 'off')])
  212. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'off')
  213. check_attr_val(topology, CONFIG_DN, 'passwordCheckSyntax', 'on')
  214. topology.standalone.simple_bind_s(BN, PASSWORD)
  215. try:
  216. topology.standalone.add_s(Entry(('cn=test3,%s' % OU_PEOPLE,
  217. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  218. 'cn': 'test3',
  219. 'sn': 'test3',
  220. 'userPassword': 'Abcd3456'})))
  221. except ldap.LDAPError as e:
  222. log.fatal('Adding cn=test3 with "userPassword: Abcd3456" failed: ' + e.message['desc'])
  223. assert False
  224. log.info('PASSED')
  225. def _142_run_4(topology):
  226. """
  227. Set 'passwordCheckSyntax: on'
  228. Set 'nsslapd-pwpolicy-inherit-global: on'
  229. Set password syntax to fine-grained password policy to check it overrides the global settings.
  230. """
  231. _header(topology, 'Case 4 - Make sure an entry added to ou=people follows the fine-grained password syntax restrictions.')
  232. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  233. topology.standalone.modify_s(CONFIG_DN,
  234. [(ldap.MOD_REPLACE, ATTR_INHERIT_GLOBAL, 'on')])
  235. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'on')
  236. check_attr_val(topology, CONFIG_DN, 'passwordCheckSyntax', 'on')
  237. topology.standalone.modify_s('cn="%s",%s' % (PWP_ENTRY_DN, PWP_CONTAINER_DN),
  238. [(ldap.MOD_REPLACE, 'passwordMinLength', '5'),
  239. (ldap.MOD_REPLACE, 'passwordMinCategories', '2')])
  240. try:
  241. topology.standalone.add_s(Entry(('cn=test4,%s' % OU_PEOPLE,
  242. {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
  243. 'cn': 'test4',
  244. 'sn': 'test4',
  245. 'userPassword': 'Abcd4'})))
  246. except ldap.LDAPError as e:
  247. log.fatal('Adding cn=test4 with "userPassword: Abcd4" failed: ' + e.message['desc'])
  248. assert False
  249. log.info('PASSED')
  250. def test_ticket142(topology):
  251. '''
  252. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  253. To run isolated without py.test, you need to
  254. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  255. - set the installation prefix
  256. - run this program
  257. '''
  258. global installation_prefix
  259. installation_prefix = None
  260. _142_init(topology)
  261. _142_run_0(topology)
  262. _142_run_1(topology)
  263. _142_run_2(topology)
  264. _142_run_3(topology)
  265. _142_run_4(topology)
  266. if __name__ == '__main__':
  267. # Run isolated
  268. # -s for DEBUG mode
  269. CURRENT_FILE = os.path.realpath(__file__)
  270. pytest.main("-s %s" % CURRENT_FILE)