1
0

ticket47462_test.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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 sys
  10. import time
  11. import ldap
  12. import logging
  13. import pytest
  14. from lib389 import DirSrv, Entry, tools
  15. from lib389.tools import DirSrvTools
  16. from lib389._constants import *
  17. from lib389.properties import *
  18. logging.getLogger(__name__).setLevel(logging.DEBUG)
  19. log = logging.getLogger(__name__)
  20. #
  21. # important part. We can deploy Master1 and Master2 on different versions
  22. #
  23. installation1_prefix = None
  24. installation2_prefix = None
  25. DES_PLUGIN = 'cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config'
  26. AES_PLUGIN = 'cn=AES,cn=Password Storage Schemes,cn=plugins,cn=config'
  27. MMR_PLUGIN = 'cn=Multimaster Replication Plugin,cn=plugins,cn=config'
  28. AGMT_DN = ''
  29. USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
  30. USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
  31. TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
  32. DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config'
  33. class TopologyMaster1Master2(object):
  34. def __init__(self, master1, master2):
  35. master1.open()
  36. self.master1 = master1
  37. master2.open()
  38. self.master2 = master2
  39. @pytest.fixture(scope="module")
  40. def topology(request):
  41. '''
  42. This fixture is used to create a replicated topology for the 'module'.
  43. The replicated topology is MASTER1 <-> Master2.
  44. '''
  45. global installation1_prefix
  46. global installation2_prefix
  47. # allocate master1 on a given deployement
  48. master1 = DirSrv(verbose=False)
  49. if installation1_prefix:
  50. args_instance[SER_DEPLOYED_DIR] = installation1_prefix
  51. # Args for the master1 instance
  52. args_instance[SER_HOST] = HOST_MASTER_1
  53. args_instance[SER_PORT] = PORT_MASTER_1
  54. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  55. args_master = args_instance.copy()
  56. master1.allocate(args_master)
  57. # allocate master1 on a given deployement
  58. master2 = DirSrv(verbose=False)
  59. if installation2_prefix:
  60. args_instance[SER_DEPLOYED_DIR] = installation2_prefix
  61. # Args for the consumer instance
  62. args_instance[SER_HOST] = HOST_MASTER_2
  63. args_instance[SER_PORT] = PORT_MASTER_2
  64. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
  65. args_master = args_instance.copy()
  66. master2.allocate(args_master)
  67. # Get the status of the instance and restart it if it exists
  68. instance_master1 = master1.exists()
  69. instance_master2 = master2.exists()
  70. # Remove all the instances
  71. if instance_master1:
  72. master1.delete()
  73. if instance_master2:
  74. master2.delete()
  75. # Create the instances
  76. master1.create()
  77. master1.open()
  78. master2.create()
  79. master2.open()
  80. #
  81. # Now prepare the Master-Consumer topology
  82. #
  83. # First Enable replication
  84. master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  85. master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
  86. # Initialize the supplier->consumer
  87. properties = {RA_NAME: r'meTo_$host:$port',
  88. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  89. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  90. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  91. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  92. AGMT_DN = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
  93. master1.agreement
  94. if not AGMT_DN:
  95. log.fatal("Fail to create a replica agreement")
  96. sys.exit(1)
  97. log.debug("%s created" % AGMT_DN)
  98. properties = {RA_NAME: r'meTo_$host:$port',
  99. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  100. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  101. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  102. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  103. master2.agreement.create(suffix=DEFAULT_SUFFIX, host=master1.host, port=master1.port, properties=properties)
  104. master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
  105. master1.waitForReplInit(AGMT_DN)
  106. # Check replication is working fine
  107. if master1.testReplication(DEFAULT_SUFFIX, master2):
  108. log.info('Replication is working.')
  109. else:
  110. log.fatal('Replication is not working.')
  111. assert False
  112. # clear the tmp directory
  113. master1.clearTmpDir(__file__)
  114. def fin():
  115. master1.delete()
  116. master2.delete()
  117. request.addfinalizer(fin)
  118. return TopologyMaster1Master2(master1, master2)
  119. def test_ticket47462(topology):
  120. """
  121. Test that AES properly replaces DES during an update/restart, and that
  122. replication also works correctly.
  123. """
  124. #
  125. # First set config as if it's an older version. Set DES to use
  126. # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
  127. # and set a DES password for the replication agreement.
  128. #
  129. # Add an extra attribute to the DES plugin args
  130. #
  131. try:
  132. topology.master1.modify_s(DES_PLUGIN,
  133. [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
  134. except ldap.LDAPError as e:
  135. log.fatal('Failed to enable DES plugin, error: ' +
  136. e.message['desc'])
  137. assert False
  138. try:
  139. topology.master1.modify_s(DES_PLUGIN,
  140. [(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
  141. except ldap.LDAPError as e:
  142. log.fatal('Failed to reset DES plugin, error: ' +
  143. e.message['desc'])
  144. assert False
  145. try:
  146. topology.master1.modify_s(MMR_PLUGIN,
  147. [(ldap.MOD_DELETE,
  148. 'nsslapd-plugin-depends-on-named',
  149. 'AES')])
  150. except ldap.NO_SUCH_ATTRIBUTE:
  151. pass
  152. except ldap.LDAPError as e:
  153. log.fatal('Failed to reset MMR plugin, error: ' +
  154. e.message['desc'])
  155. assert False
  156. #
  157. # Delete the AES plugin
  158. #
  159. try:
  160. topology.master1.delete_s(AES_PLUGIN)
  161. except ldap.NO_SUCH_OBJECT:
  162. pass
  163. except ldap.LDAPError as e:
  164. log.fatal('Failed to delete AES plugin, error: ' +
  165. e.message['desc'])
  166. assert False
  167. # restart the server so we must use DES plugin
  168. topology.master1.restart(timeout=10)
  169. #
  170. # Get the agmt dn, and set the password
  171. #
  172. try:
  173. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
  174. 'objectclass=nsDS5ReplicationAgreement')
  175. if entry:
  176. agmt_dn = entry[0].dn
  177. log.info('Found agmt dn (%s)' % agmt_dn)
  178. else:
  179. log.fatal('No replication agreements!')
  180. assert False
  181. except ldap.LDAPError as e:
  182. log.fatal('Failed to search for replica credentials: ' +
  183. e.message['desc'])
  184. assert False
  185. try:
  186. properties = {RA_BINDPW: "password"}
  187. topology.master1.agreement.setProperties(None, agmt_dn, None,
  188. properties)
  189. log.info('Successfully modified replication agreement')
  190. except ValueError:
  191. log.error('Failed to update replica agreement: ' + AGMT_DN)
  192. assert False
  193. #
  194. # Check replication works with the new DES password
  195. #
  196. try:
  197. topology.master1.add_s(Entry((USER1_DN,
  198. {'objectclass': "top person".split(),
  199. 'sn': 'sn',
  200. 'description': 'DES value to convert',
  201. 'cn': 'test_user'})))
  202. loop = 0
  203. ent = None
  204. while loop <= 10:
  205. try:
  206. ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE,
  207. "(objectclass=*)")
  208. break
  209. except ldap.NO_SUCH_OBJECT:
  210. time.sleep(1)
  211. loop += 1
  212. if not ent:
  213. log.fatal('Replication test failed fo user1!')
  214. assert False
  215. else:
  216. log.info('Replication test passed')
  217. except ldap.LDAPError as e:
  218. log.fatal('Failed to add test user: ' + e.message['desc'])
  219. assert False
  220. #
  221. # Add a backend (that has no entries)
  222. #
  223. try:
  224. topology.master1.backend.create("o=empty", {BACKEND_NAME: "empty"})
  225. except ldap.LDAPError as e:
  226. log.fatal('Failed to create extra/empty backend: ' + e.message['desc'])
  227. assert False
  228. #
  229. # Run the upgrade...
  230. #
  231. topology.master1.upgrade('online')
  232. topology.master1.restart()
  233. topology.master2.restart()
  234. #
  235. # Check that the restart converted existing DES credentials
  236. #
  237. try:
  238. entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
  239. 'nsDS5ReplicaCredentials=*')
  240. if entry:
  241. val = entry[0].getValue('nsDS5ReplicaCredentials')
  242. if val.startswith('{AES-'):
  243. log.info('The DES credentials have been converted to AES')
  244. else:
  245. log.fatal('Failed to convert credentials from DES to AES!')
  246. assert False
  247. else:
  248. log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
  249. assert False
  250. except ldap.LDAPError as e:
  251. log.fatal('Failed to search for replica credentials: ' +
  252. e.message['desc'])
  253. assert False
  254. #
  255. # Check that the AES plugin exists, and has all the attributes listed in
  256. # DES plugin. The attributes might not be in the expected order so check
  257. # all the attributes.
  258. #
  259. try:
  260. entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE,
  261. 'objectclass=*')
  262. if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
  263. not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
  264. not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
  265. log.fatal('The AES plugin did not have the DES attribute copied ' +
  266. 'over correctly')
  267. assert False
  268. else:
  269. log.info('The AES plugin was correctly setup')
  270. except ldap.LDAPError as e:
  271. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  272. assert False
  273. #
  274. # Check that the MMR plugin was updated
  275. #
  276. try:
  277. entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE,
  278. 'objectclass=*')
  279. if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
  280. log.fatal('The MMR Plugin was not correctly updated')
  281. assert False
  282. else:
  283. log.info('The MMR plugin was correctly updated')
  284. except ldap.LDAPError as e:
  285. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  286. assert False
  287. #
  288. # Check that the DES plugin was correctly updated
  289. #
  290. try:
  291. entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE,
  292. 'objectclass=*')
  293. if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
  294. log.fatal('The DES Plugin was not correctly updated')
  295. assert False
  296. else:
  297. log.info('The DES plugin was correctly updated')
  298. except ldap.LDAPError as e:
  299. log.fatal('Failed to find AES plugin: ' + e.message['desc'])
  300. assert False
  301. #
  302. # Check replication one last time
  303. #
  304. try:
  305. topology.master1.add_s(Entry((USER_DN,
  306. {'objectclass': "top person".split(),
  307. 'sn': 'sn',
  308. 'cn': 'test_user'})))
  309. loop = 0
  310. ent = None
  311. while loop <= 10:
  312. try:
  313. ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE,
  314. "(objectclass=*)")
  315. break
  316. except ldap.NO_SUCH_OBJECT:
  317. time.sleep(1)
  318. loop += 1
  319. if not ent:
  320. log.fatal('Replication test failed!')
  321. assert False
  322. else:
  323. log.info('Replication test passed')
  324. except ldap.LDAPError as e:
  325. log.fatal('Failed to add test user: ' + e.message['desc'])
  326. assert False
  327. # Check the entry
  328. log.info('Entry before running task...')
  329. try:
  330. entry = topology.master1.search_s(USER1_DN,
  331. ldap.SCOPE_BASE,
  332. 'objectclass=*')
  333. if entry:
  334. print(str(entry))
  335. else:
  336. log.fatal('Failed to find entries')
  337. assert False
  338. except ldap.LDAPError as e:
  339. log.fatal('Failed to search for entries: ' +
  340. e.message['desc'])
  341. assert False
  342. #
  343. # Test the DES2AES Task on USER1_DN
  344. #
  345. try:
  346. topology.master1.add_s(Entry((DES2AES_TASK_DN,
  347. {'objectclass': ['top',
  348. 'extensibleObject'],
  349. 'suffix': DEFAULT_SUFFIX,
  350. 'cn': 'convert'})))
  351. except ldap.LDAPError as e:
  352. log.fatal('Failed to add task entry: ' + e.message['desc'])
  353. assert False
  354. # Wait for task
  355. task_entry = Entry(DES2AES_TASK_DN)
  356. (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True)
  357. if exitCode:
  358. log.fatal("Error: des2aes task exited with %d" % (exitCode))
  359. assert False
  360. # Check the entry
  361. try:
  362. entry = topology.master1.search_s(USER1_DN,
  363. ldap.SCOPE_BASE,
  364. 'objectclass=*')
  365. if entry:
  366. val = entry[0].getValue('description')
  367. print(str(entry[0]))
  368. if val.startswith('{AES-'):
  369. log.info('Task: DES credentials have been converted to AES')
  370. else:
  371. log.fatal('Task: Failed to convert credentials from DES to ' +
  372. 'AES! (%s)' % (val))
  373. assert False
  374. else:
  375. log.fatal('Failed to find entries')
  376. assert False
  377. except ldap.LDAPError as e:
  378. log.fatal('Failed to search for entries: ' +
  379. e.message['desc'])
  380. assert False
  381. if __name__ == '__main__':
  382. # Run isolated
  383. # -s for DEBUG mode
  384. CURRENT_FILE = os.path.realpath(__file__)
  385. pytest.main("-s %s" % CURRENT_FILE)