pwdPolicy_inherit_global_test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. # --- BEGIN COPYRIGHT BLOCK ---
  2. # Copyright (C) 2016 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. import subprocess
  17. from lib389 import DirSrv, Entry, tools
  18. from lib389 import DirSrvTools
  19. from lib389.tools import DirSrvTools
  20. from lib389._constants import *
  21. from lib389.properties import *
  22. log = logging.getLogger(__name__)
  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_CONTAINER_PEOPLE = 'cn="%s",%s' % (PWP_ENTRY_DN, PWP_CONTAINER_DN)
  29. PWP_TEMPLATE_ENTRY_DN = 'cn=nsPwTemplateEntry,' + OU_PEOPLE
  30. ATTR_INHERIT_GLOBAL = 'nsslapd-pwpolicy-inherit-global'
  31. ATTR_CHECK_SYNTAX = 'passwordCheckSyntax'
  32. BN = 'uid=buser,' + DEFAULT_SUFFIX
  33. TEMP_USER = 'cn=test{}'
  34. TEMP_USER_DN = '%s,%s' % (TEMP_USER, OU_PEOPLE)
  35. class TopologyStandalone(object):
  36. def __init__(self, standalone):
  37. standalone.open()
  38. self.standalone = standalone
  39. @pytest.fixture(scope="module")
  40. def topology(request):
  41. """This fixture is used to standalone topology for the 'module'."""
  42. standalone = DirSrv(verbose=False)
  43. # Args for the standalone instance
  44. args_instance[SER_HOST] = HOST_STANDALONE
  45. args_instance[SER_PORT] = PORT_STANDALONE
  46. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  47. args_standalone = args_instance.copy()
  48. standalone.allocate(args_standalone)
  49. # Get the status of the instance and restart it if it exists
  50. instance_standalone = standalone.exists()
  51. # Remove the instance
  52. if instance_standalone:
  53. standalone.delete()
  54. # Create the instance
  55. standalone.create()
  56. # Used to retrieve configuration information (dbdir, confdir...)
  57. standalone.open()
  58. def fin():
  59. standalone.delete()
  60. request.addfinalizer(fin)
  61. # Here we have standalone instance up and running
  62. return TopologyStandalone(standalone)
  63. @pytest.fixture(scope="module")
  64. def test_user(topology, request):
  65. """User for binding operation"""
  66. log.info('Adding user {}'.format(BN))
  67. try:
  68. topology.standalone.add_s(Entry((BN,
  69. {'objectclass': ['top',
  70. 'person',
  71. 'organizationalPerson',
  72. 'inetOrgPerson'],
  73. 'cn': 'bind user',
  74. 'sn': 'bind user',
  75. 'userPassword': PASSWORD})))
  76. log.info('Adding an aci for the bind user')
  77. BN_ACI = '(targetattr="*")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///%s";)' % BN
  78. topology.standalone.modify_s(OU_PEOPLE, [(ldap.MOD_ADD, 'aci', BN_ACI)])
  79. except ldap.LDAPError as e:
  80. log.error('Failed to add user (%s): error (%s)' % (BN,
  81. e.message['desc']))
  82. raise e
  83. def fin():
  84. log.info('Deleting user {}'.format(BN))
  85. topology.standalone.delete_s(BN)
  86. topology.standalone.modify_s(OU_PEOPLE, [(ldap.MOD_DELETE, 'aci', BN_ACI)])
  87. request.addfinalizer(fin)
  88. @pytest.fixture(scope="module")
  89. def password_policy(topology, test_user):
  90. """Set global password policy.
  91. Then, set fine-grained subtree level password policy
  92. to ou=People with no password syntax.
  93. Note: do not touch nsslapd-pwpolicy-inherit-global -- off by default
  94. """
  95. log.info('Enable fine-grained policy')
  96. try:
  97. topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE,
  98. 'nsslapd-pwpolicy-local',
  99. 'on')])
  100. except ldap.LDAPError as e:
  101. log.error('Failed to set fine-grained policy: error {}'.format(
  102. e.message['desc']))
  103. raise e
  104. log.info('Create password policy for subtree {}'.format(OU_PEOPLE))
  105. try:
  106. subprocess.call(['ns-newpwpolicy.pl', '-D', DN_DM, '-w', PASSWORD,
  107. '-p', str(PORT_STANDALONE), '-h', HOST_STANDALONE,
  108. '-S', OU_PEOPLE, '-Z', SERVERID_STANDALONE])
  109. except subprocess.CalledProcessError as e:
  110. log.error('Failed to create pw policy policy for {}: error {}'.format(
  111. OU_PEOPLE, e.message['desc']))
  112. raise e
  113. log.info('Add pwdpolicysubentry attribute to {}'.format(OU_PEOPLE))
  114. try:
  115. topology.standalone.modify_s(OU_PEOPLE, [(ldap.MOD_REPLACE,
  116. 'pwdpolicysubentry',
  117. PWP_CONTAINER_PEOPLE)])
  118. except ldap.LDAPError as e:
  119. log.error('Failed to pwdpolicysubentry pw policy '\
  120. 'policy for {}: error {}'.format(OU_PEOPLE,
  121. e.message['desc']))
  122. raise e
  123. log.info("Set the default settings for the policy container.")
  124. topology.standalone.modify_s(PWP_CONTAINER_PEOPLE,
  125. [(ldap.MOD_REPLACE, 'passwordMustChange', 'off'),
  126. (ldap.MOD_REPLACE, 'passwordExp', 'off'),
  127. (ldap.MOD_REPLACE, 'passwordMinAge', '0'),
  128. (ldap.MOD_REPLACE, 'passwordChange', 'off'),
  129. (ldap.MOD_REPLACE, 'passwordStorageScheme', 'ssha')])
  130. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'off')
  131. check_attr_val(topology, CONFIG_DN, ATTR_CHECK_SYNTAX, 'off')
  132. def check_attr_val(topology, dn, attr, expected):
  133. """Check that entry has the value"""
  134. try:
  135. centry = topology.standalone.search_s(dn, ldap.SCOPE_BASE, 'cn=*')
  136. assert centry[0], 'Failed to get %s' % dn
  137. val = centry[0].getValue(attr)
  138. assert val == expected, 'Default value of %s is not %s, but %s' % (
  139. attr, expected, val)
  140. log.info('Default value of %s is %s' % (attr, expected))
  141. except ldap.LDAPError as e:
  142. log.fatal('Failed to search ' + dn + ': ' + e.message['desc'])
  143. raise e
  144. @pytest.mark.parametrize('inherit_value,checksyntax_value',
  145. [('off', 'off'), ('on', 'off'), ('off', 'on')])
  146. def test_entry_has_no_restrictions(topology, password_policy, test_user,
  147. inherit_value, checksyntax_value):
  148. """Make sure an entry added to ou=people
  149. has no password syntax restrictions when:
  150. - 'passwordCheckSyntax' is 'off' for 'nsslapd-pwpolicy-inherit-global'
  151. equaled 'off' and 'on'
  152. - 'passwordCheckSyntax' is 'on' for 'nsslapd-pwpolicy-inherit-global'
  153. equaled 'off'
  154. :Feature: Password policy
  155. :Setup: Standalone instance, test user,
  156. password policy entries for a subtree
  157. :Steps: 1. Bind as test user
  158. 2. Set 'nsslapd-pwpolicy-inherit-global' and
  159. 'passwordCheckSyntax' accordingly:
  160. a) 'off' and 'off'
  161. b) 'on' and 'off'
  162. c) 'off' and 'on'
  163. 3. Try to add user with a short password
  164. :Assert: No exception should occure
  165. """
  166. log.info('Set {} to {}'.format(ATTR_INHERIT_GLOBAL, inherit_value))
  167. log.info('Set {} to {}'.format(ATTR_CHECK_SYNTAX, checksyntax_value))
  168. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE,
  169. ATTR_INHERIT_GLOBAL, inherit_value)])
  170. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE,
  171. ATTR_CHECK_SYNTAX, checksyntax_value)])
  172. # Wait a second for cn=config to apply
  173. time.sleep(1)
  174. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, inherit_value)
  175. check_attr_val(topology, CONFIG_DN, ATTR_CHECK_SYNTAX, checksyntax_value)
  176. log.info('Bind as test user')
  177. topology.standalone.simple_bind_s(BN, PASSWORD)
  178. log.info('Make sure an entry added to ou=people has '
  179. 'no password syntax restrictions.')
  180. try:
  181. topology.standalone.add_s(Entry((TEMP_USER_DN.format('0'),
  182. {'objectclass': ['top',
  183. 'person',
  184. 'organizationalPerson',
  185. 'inetOrgPerson'],
  186. 'cn': TEMP_USER.format('0'),
  187. 'sn': TEMP_USER.format('0'),
  188. 'userPassword': 'short'})))
  189. except ldap.LDAPError as e:
  190. log.fatal('Failed to add cn=test0 with userPassword: short: ' +
  191. e.message['desc'])
  192. raise e
  193. finally:
  194. log.info('Bind as DM user')
  195. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  196. log.info('Remove {}'.format(TEMP_USER_DN.format('0')))
  197. try:
  198. topology.standalone.delete_s(TEMP_USER_DN.format('0'))
  199. except ldap.NO_SUCH_OBJECT as e:
  200. log.fatal('There is no {}, it is a problem'.format(TEMP_USER_DN.format('0')))
  201. raise e
  202. @pytest.mark.parametrize('container', [DN_CONFIG, PWP_CONTAINER_PEOPLE])
  203. def test_entry_has_restrictions(topology, password_policy, test_user, container):
  204. """Set 'nsslapd-pwpolicy-inherit-global: on'
  205. and 'passwordCheckSyntax: on'. Make sure that
  206. syntax rules work, if set them at both: cn=config and
  207. ou=people policy container.
  208. :Feature: Password policy
  209. :Setup: Standalone instance, test user,
  210. password policy entries for a subtree
  211. :Steps: 1. Bind as test user
  212. 2. Switch 'nsslapd-pwpolicy-inherit-global: on'
  213. 3. Switch 'passwordCheckSyntax: on'
  214. 4. Set 'passwordMinLength: 9' to:
  215. a) cn=config
  216. b) ou=people policy container
  217. 5. Try to add user with a short password (<9)
  218. 6. Try to add user with a long password (>9)
  219. :Assert: User should be rejected
  220. """
  221. log.info('Set {} to {}'.format(ATTR_INHERIT_GLOBAL, 'on'))
  222. log.info('Set {} to {}'.format(ATTR_CHECK_SYNTAX, 'on'))
  223. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE,
  224. ATTR_INHERIT_GLOBAL, 'on')])
  225. topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE,
  226. ATTR_CHECK_SYNTAX, 'on')])
  227. topology.standalone.modify_s(container, [(ldap.MOD_REPLACE,
  228. 'passwordMinLength' , '9')])
  229. # Wait a second for cn=config to apply
  230. time.sleep(1)
  231. check_attr_val(topology, CONFIG_DN, ATTR_INHERIT_GLOBAL, 'on')
  232. check_attr_val(topology, CONFIG_DN, ATTR_CHECK_SYNTAX, 'on')
  233. log.info('Bind as test user')
  234. topology.standalone.simple_bind_s(BN, PASSWORD)
  235. log.info('Try to add user with a short password (<9)')
  236. with pytest.raises(ldap.CONSTRAINT_VIOLATION):
  237. topology.standalone.add_s(Entry((TEMP_USER_DN.format('0'),
  238. {'objectclass': ['top',
  239. 'person',
  240. 'organizationalPerson',
  241. 'inetOrgPerson'],
  242. 'cn': TEMP_USER.format('0'),
  243. 'sn': TEMP_USER.format('0'),
  244. 'userPassword': 'short'})))
  245. log.info('Try to add user with a long password (>9)')
  246. try:
  247. topology.standalone.add_s(Entry((TEMP_USER_DN.format('1'),
  248. {'objectclass': ['top',
  249. 'person',
  250. 'organizationalPerson',
  251. 'inetOrgPerson'],
  252. 'cn': TEMP_USER.format('1'),
  253. 'sn': TEMP_USER.format('1'),
  254. 'userPassword': 'Reallylong1'})))
  255. except ldap.LDAPError as e:
  256. log.fatal('Failed to add cn=test1 with userPassword: short: '
  257. + e.message['desc'])
  258. raise e
  259. finally:
  260. log.info('Bind as DM user')
  261. topology.standalone.simple_bind_s(DN_DM, PASSWORD)
  262. log.info('Remove {}'.format(TEMP_USER_DN.format('0')))
  263. try:
  264. topology.standalone.delete_s(TEMP_USER_DN.format('0'))
  265. except ldap.NO_SUCH_OBJECT as e:
  266. log.info('There is no {}, it is okay'.format(TEMP_USER_DN.format('0')))
  267. try:
  268. topology.standalone.delete_s(TEMP_USER_DN.format('1'))
  269. except ldap.NO_SUCH_OBJECT as e:
  270. log.fatal('There is no {}, it is a problem'.format(TEMP_USER_DN.format('1')))
  271. raise e
  272. if __name__ == '__main__':
  273. # Run isolated
  274. # -s for DEBUG mode
  275. CURRENT_FILE = os.path.realpath(__file__)
  276. pytest.main("-s %s" % CURRENT_FILE)