ticket48759_test.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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. log = logging.getLogger(__name__)
  21. MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
  22. GROUP_DN = ("cn=group," + DEFAULT_SUFFIX)
  23. MEMBER_DN_COMP = "uid=member"
  24. class TopologyStandalone(object):
  25. def __init__(self, standalone):
  26. standalone.open()
  27. self.standalone = standalone
  28. @pytest.fixture(scope="module")
  29. def topology(request):
  30. '''
  31. This fixture is used to standalone topology for the 'module'.
  32. '''
  33. standalone = DirSrv(verbose=False)
  34. # Args for the standalone instance
  35. args_instance[SER_HOST] = HOST_STANDALONE
  36. args_instance[SER_PORT] = PORT_STANDALONE
  37. args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
  38. args_standalone = args_instance.copy()
  39. standalone.allocate(args_standalone)
  40. # Get the status of the instance and restart it if it exists
  41. instance_standalone = standalone.exists()
  42. # Remove the instance
  43. if instance_standalone:
  44. standalone.delete()
  45. # Create the instance
  46. standalone.create()
  47. # Used to retrieve configuration information (dbdir, confdir...)
  48. standalone.open()
  49. def fin():
  50. standalone.delete()
  51. request.addfinalizer(fin)
  52. # Here we have standalone instance up and running
  53. return TopologyStandalone(standalone)
  54. def _add_group_with_members(topology):
  55. # Create group
  56. try:
  57. topology.standalone.add_s(Entry((GROUP_DN,
  58. {'objectclass': 'top groupofnames'.split(),
  59. 'cn': 'group'})))
  60. except ldap.LDAPError as e:
  61. log.fatal('Failed to add group: error ' + e.message['desc'])
  62. assert False
  63. # Add members to the group - set timeout
  64. log.info('Adding members to the group...')
  65. for idx in range(1, 5):
  66. try:
  67. MEMBER_VAL = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
  68. topology.standalone.modify_s(GROUP_DN,
  69. [(ldap.MOD_ADD,
  70. 'member',
  71. MEMBER_VAL)])
  72. except ldap.LDAPError as e:
  73. log.fatal('Failed to update group: member (%s) - error: %s' %
  74. (MEMBER_VAL, e.message['desc']))
  75. assert False
  76. def _find_retrocl_changes(topology, user_dn=None):
  77. ents = topology.standalone.search_s('cn=changelog', ldap.SCOPE_SUBTREE, '(targetDn=%s)' %user_dn)
  78. return len(ents)
  79. def _find_memberof(topology, user_dn=None, group_dn=None, find_result=True):
  80. ent = topology.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
  81. found = False
  82. if ent.hasAttr('memberof'):
  83. for val in ent.getValues('memberof'):
  84. topology.standalone.log.info("!!!!!!! %s: memberof->%s" % (user_dn, val))
  85. if val == group_dn:
  86. found = True
  87. break
  88. if find_result:
  89. assert(found)
  90. else:
  91. assert(not found)
  92. def test_ticket48759(topology):
  93. """
  94. The fix for ticket 48759 has to prevent plugin calls for tombstone purging
  95. The test uses the memberof and retrocl plugins to verify this.
  96. In tombstone purging without the fix the mmeberof plugin is called,
  97. if the tombstone entry is a group,
  98. it modifies the user entries for the group
  99. and if retrocl is enabled this mod is written to the retrocl
  100. The test sequence is:
  101. - enable replication
  102. - enable memberof and retro cl plugin
  103. - add user entries
  104. - add a group and add the users as members
  105. - verify memberof is set to users
  106. - delete the group
  107. - verify memberof is removed from users
  108. - add group again
  109. - verify memberof is set to users
  110. - get number of changes in retro cl for one user
  111. - configure tombstone purging
  112. - wait for purge interval to pass
  113. - add a dummy entry to increase maxcsn
  114. - wait for purge interval to pass two times
  115. - get number of changes in retro cl for user again
  116. - assert there was no additional change
  117. """
  118. log.info('Testing Ticket 48759 - no plugin calls for tombstone purging')
  119. #
  120. # Setup Replication
  121. #
  122. log.info('Setting up replication...')
  123. topology.standalone.replica.enableReplication(suffix=DEFAULT_SUFFIX, role=REPLICAROLE_MASTER,
  124. replicaId=REPLICAID_MASTER_1)
  125. #
  126. # enable dynamic plugins, memberof and retro cl plugin
  127. #
  128. log.info('Enable plugins...')
  129. try:
  130. topology.standalone.modify_s(DN_CONFIG,
  131. [(ldap.MOD_REPLACE,
  132. 'nsslapd-dynamic-plugins',
  133. 'on')])
  134. except ldap.LDAPError as e:
  135. ldap.error('Failed to enable dynamic plugins! ' + e.message['desc'])
  136. assert False
  137. topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
  138. topology.standalone.plugins.enable(name=PLUGIN_RETRO_CHANGELOG)
  139. # Configure memberOf group attribute
  140. try:
  141. topology.standalone.modify_s(MEMBEROF_PLUGIN_DN,
  142. [(ldap.MOD_REPLACE,
  143. 'memberofgroupattr',
  144. 'member')])
  145. except ldap.LDAPError as e:
  146. log.fatal('Failed to configure memberOf plugin: error ' + e.message['desc'])
  147. assert False
  148. #
  149. # create some users and a group
  150. #
  151. log.info('create users and group...')
  152. for idx in range(1, 5):
  153. try:
  154. USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
  155. topology.standalone.add_s(Entry((USER_DN,
  156. {'objectclass': 'top extensibleObject'.split(),
  157. 'uid': 'member%d' % (idx)})))
  158. except ldap.LDAPError as e:
  159. log.fatal('Failed to add user (%s): error %s' % (USER_DN, e.message['desc']))
  160. assert False
  161. _add_group_with_members(topology)
  162. MEMBER_VAL = ("uid=member2,%s" % DEFAULT_SUFFIX)
  163. time.sleep(1)
  164. _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
  165. # delete group
  166. log.info('delete group...')
  167. try:
  168. topology.standalone.delete_s(GROUP_DN)
  169. except ldap.LDAPError as e:
  170. log.error('Failed to delete entry: ' + e.message['desc'])
  171. assert False
  172. time.sleep(1)
  173. _find_memberof(topology, MEMBER_VAL, GROUP_DN, False)
  174. # add group again
  175. log.info('add group again')
  176. _add_group_with_members(topology)
  177. time.sleep(1)
  178. _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
  179. #
  180. # get number of changelog records for one user entry
  181. log.info('get number of changes for %s before tombstone purging' % MEMBER_VAL)
  182. changes_pre = _find_retrocl_changes(topology, MEMBER_VAL)
  183. # configure tombstone purging
  184. args = {REPLICA_PRECISE_PURGING: 'on',
  185. REPLICA_PURGE_DELAY: '5',
  186. REPLICA_PURGE_INTERVAL: '5'}
  187. try:
  188. topology.standalone.replica.setProperties(DEFAULT_SUFFIX, None, None, args)
  189. except:
  190. log.fatal('Failed to configure replica')
  191. assert False
  192. # Wait for the interval to pass
  193. log.info('Wait for tombstone purge interval to pass ...')
  194. time.sleep(6)
  195. # Add an entry to trigger replication
  196. log.info('add dummy entry')
  197. try:
  198. topology.standalone.add_s(Entry(('cn=test_entry,dc=example,dc=com', {
  199. 'objectclass': 'top person'.split(),
  200. 'sn': 'user',
  201. 'cn': 'entry1'})))
  202. except ldap.LDAPError as e:
  203. log.error('Failed to add entry: ' + e.message['desc'])
  204. assert False
  205. # check memberof is still correct
  206. time.sleep(1)
  207. _find_memberof(topology, MEMBER_VAL, GROUP_DN, True)
  208. # Wait for the interval to pass again
  209. log.info('Wait for tombstone purge interval to pass again...')
  210. time.sleep(10)
  211. #
  212. # get number of changelog records for one user entry
  213. log.info('get number of changes for %s before tombstone purging' % MEMBER_VAL)
  214. changes_post = _find_retrocl_changes(topology, MEMBER_VAL)
  215. assert (changes_pre == changes_post)
  216. if __name__ == '__main__':
  217. # Run isolated
  218. # -s for DEBUG mode
  219. CURRENT_FILE = os.path.realpath(__file__)
  220. pytest.main("-s %s" % CURRENT_FILE)