ticket47553_ger.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. '''
  2. Created on Nov 7, 2013
  3. @author: tbordaz
  4. '''
  5. import os
  6. import sys
  7. import time
  8. import ldap
  9. import logging
  10. import pytest
  11. from lib389 import DirSrv, Entry, tools
  12. from lib389.tools import DirSrvTools
  13. from lib389._constants import *
  14. from lib389.properties import *
  15. from ldap.controls.simple import GetEffectiveRightsControl
  16. logging.getLogger(__name__).setLevel(logging.DEBUG)
  17. log = logging.getLogger(__name__)
  18. #
  19. # important part. We can deploy Master1 and Master2 on different versions
  20. #
  21. installation1_prefix = None
  22. installation2_prefix = None
  23. TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
  24. STAGING_CN = "staged user"
  25. PRODUCTION_CN = "accounts"
  26. EXCEPT_CN = "excepts"
  27. STAGING_DN = "cn=%s,%s" % (STAGING_CN, SUFFIX)
  28. PRODUCTION_DN = "cn=%s,%s" % (PRODUCTION_CN, SUFFIX)
  29. PROD_EXCEPT_DN = "cn=%s,%s" % (EXCEPT_CN, PRODUCTION_DN)
  30. STAGING_PATTERN = "cn=%s*,%s" % (STAGING_CN[:2], SUFFIX)
  31. PRODUCTION_PATTERN = "cn=%s*,%s" % (PRODUCTION_CN[:2], SUFFIX)
  32. BAD_STAGING_PATTERN = "cn=bad*,%s" % (SUFFIX)
  33. BAD_PRODUCTION_PATTERN = "cn=bad*,%s" % (SUFFIX)
  34. BIND_CN = "bind_entry"
  35. BIND_DN = "cn=%s,%s" % (BIND_CN, SUFFIX)
  36. BIND_PW = "password"
  37. NEW_ACCOUNT = "new_account"
  38. MAX_ACCOUNTS = 20
  39. CONFIG_MODDN_ACI_ATTR = "nsslapd-moddn-aci"
  40. class TopologyMaster1Master2(object):
  41. def __init__(self, master1, master2):
  42. master1.open()
  43. self.master1 = master1
  44. master2.open()
  45. self.master2 = master2
  46. @pytest.fixture(scope="module")
  47. def topology(request):
  48. '''
  49. This fixture is used to create a replicated topology for the 'module'.
  50. The replicated topology is MASTER1 <-> Master2.
  51. '''
  52. global installation1_prefix
  53. global installation2_prefix
  54. # allocate master1 on a given deployement
  55. master1 = DirSrv(verbose=False)
  56. if installation1_prefix:
  57. args_instance[SER_DEPLOYED_DIR] = installation1_prefix
  58. # Args for the master1 instance
  59. args_instance[SER_HOST] = HOST_MASTER_1
  60. args_instance[SER_PORT] = PORT_MASTER_1
  61. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
  62. args_master = args_instance.copy()
  63. master1.allocate(args_master)
  64. # allocate master1 on a given deployement
  65. master2 = DirSrv(verbose=False)
  66. if installation2_prefix:
  67. args_instance[SER_DEPLOYED_DIR] = installation2_prefix
  68. # Args for the consumer instance
  69. args_instance[SER_HOST] = HOST_MASTER_2
  70. args_instance[SER_PORT] = PORT_MASTER_2
  71. args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
  72. args_master = args_instance.copy()
  73. master2.allocate(args_master)
  74. # Get the status of the instance and restart it if it exists
  75. instance_master1 = master1.exists()
  76. instance_master2 = master2.exists()
  77. # Remove all the instances
  78. if instance_master1:
  79. master1.delete()
  80. if instance_master2:
  81. master2.delete()
  82. # Create the instances
  83. master1.create()
  84. master1.open()
  85. master2.create()
  86. master2.open()
  87. #
  88. # Now prepare the Master-Consumer topology
  89. #
  90. # First Enable replication
  91. master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
  92. master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
  93. # Initialize the supplier->consumer
  94. properties = {RA_NAME: r'meTo_$host:$port',
  95. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  96. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  97. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  98. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  99. repl_agreement = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
  100. if not repl_agreement:
  101. log.fatal("Fail to create a replica agreement")
  102. sys.exit(1)
  103. log.debug("%s created" % repl_agreement)
  104. properties = {RA_NAME: r'meTo_$host:$port',
  105. RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
  106. RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
  107. RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
  108. RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
  109. master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
  110. master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
  111. master1.waitForReplInit(repl_agreement)
  112. # Check replication is working fine
  113. master1.add_s(Entry((TEST_REPL_DN, {
  114. 'objectclass': "top person".split(),
  115. 'sn': 'test_repl',
  116. 'cn': 'test_repl'})))
  117. loop = 0
  118. ent = None
  119. while loop <= 10:
  120. try:
  121. ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
  122. break
  123. except ldap.NO_SUCH_OBJECT:
  124. time.sleep(1)
  125. loop += 1
  126. if ent is None:
  127. assert False
  128. # clear the tmp directory
  129. master1.clearTmpDir(__file__)
  130. # Here we have two instances master and consumer
  131. # with replication working.
  132. return TopologyMaster1Master2(master1, master2)
  133. def _bind_manager(topology):
  134. topology.master1.log.info("Bind as %s " % DN_DM)
  135. topology.master1.simple_bind_s(DN_DM, PASSWORD)
  136. def _bind_normal(topology):
  137. # bind as bind_entry
  138. topology.master1.log.info("Bind as %s" % BIND_DN)
  139. topology.master1.simple_bind_s(BIND_DN, BIND_PW)
  140. def _moddn_aci_deny_tree(topology, mod_type=None, target_from=STAGING_DN, target_to=PROD_EXCEPT_DN):
  141. '''
  142. It denies the access moddn_to in cn=except,cn=accounts,SUFFIX
  143. '''
  144. if mod_type is None:
  145. assert False
  146. ACI_TARGET_FROM = ""
  147. ACI_TARGET_TO = ""
  148. if target_from:
  149. ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from)
  150. if target_to:
  151. ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to)
  152. ACI_ALLOW = "(version 3.0; acl \"Deny MODDN to prod_except\"; deny (moddn)"
  153. ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
  154. ACI_BODY = ACI_TARGET_TO + ACI_TARGET_FROM + ACI_ALLOW + ACI_SUBJECT
  155. mod = [(mod_type, 'aci', ACI_BODY)]
  156. #topology.master1.modify_s(SUFFIX, mod)
  157. topology.master1.log.info("Add a DENY aci under %s " % PROD_EXCEPT_DN)
  158. topology.master1.modify_s(PROD_EXCEPT_DN, mod)
  159. def _moddn_aci_staging_to_production(topology, mod_type=None, target_from=STAGING_DN, target_to=PRODUCTION_DN):
  160. if mod_type is None:
  161. assert False
  162. ACI_TARGET_FROM = ""
  163. ACI_TARGET_TO = ""
  164. if target_from:
  165. ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from)
  166. if target_to:
  167. ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to)
  168. ACI_ALLOW = "(version 3.0; acl \"MODDN from staging to production\"; allow (moddn)"
  169. ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
  170. ACI_BODY = ACI_TARGET_FROM + ACI_TARGET_TO + ACI_ALLOW + ACI_SUBJECT
  171. mod = [(mod_type, 'aci', ACI_BODY)]
  172. topology.master1.modify_s(SUFFIX, mod)
  173. def _moddn_aci_from_production_to_staging(topology, mod_type=None):
  174. if mod_type is None:
  175. assert False
  176. ACI_TARGET = "(target_from = \"ldap:///%s\") (target_to = \"ldap:///%s\")" % (PRODUCTION_DN, STAGING_DN)
  177. ACI_ALLOW = "(version 3.0; acl \"MODDN from production to staging\"; allow (moddn)"
  178. ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
  179. ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
  180. mod = [(mod_type, 'aci', ACI_BODY)]
  181. topology.master1.modify_s(SUFFIX, mod)
  182. def test_ticket47553_init(topology):
  183. """
  184. Creates
  185. - a staging DIT
  186. - a production DIT
  187. - add accounts in staging DIT
  188. - enable ACL logging (commented for performance reason)
  189. """
  190. topology.master1.log.info("\n\n######################### INITIALIZATION ######################\n")
  191. # entry used to bind with
  192. topology.master1.log.info("Add %s" % BIND_DN)
  193. topology.master1.add_s(Entry((BIND_DN, {
  194. 'objectclass': "top person".split(),
  195. 'sn': BIND_CN,
  196. 'cn': BIND_CN,
  197. 'userpassword': BIND_PW})))
  198. # DIT for staging
  199. topology.master1.log.info("Add %s" % STAGING_DN)
  200. topology.master1.add_s(Entry((STAGING_DN, {
  201. 'objectclass': "top organizationalRole".split(),
  202. 'cn': STAGING_CN,
  203. 'description': "staging DIT"})))
  204. # DIT for production
  205. topology.master1.log.info("Add %s" % PRODUCTION_DN)
  206. topology.master1.add_s(Entry((PRODUCTION_DN, {
  207. 'objectclass': "top organizationalRole".split(),
  208. 'cn': PRODUCTION_CN,
  209. 'description': "production DIT"})))
  210. # DIT for production/except
  211. topology.master1.log.info("Add %s" % PROD_EXCEPT_DN)
  212. topology.master1.add_s(Entry((PROD_EXCEPT_DN, {
  213. 'objectclass': "top organizationalRole".split(),
  214. 'cn': EXCEPT_CN,
  215. 'description': "production except DIT"})))
  216. # enable acl error logging
  217. #mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')]
  218. #topology.master1.modify_s(DN_CONFIG, mod)
  219. #topology.master2.modify_s(DN_CONFIG, mod)
  220. # add dummy entries in the staging DIT
  221. for cpt in range(MAX_ACCOUNTS):
  222. name = "%s%d" % (NEW_ACCOUNT, cpt)
  223. topology.master1.add_s(Entry(("cn=%s,%s" % (name, STAGING_DN), {
  224. 'objectclass': "top person".split(),
  225. 'sn': name,
  226. 'cn': name})))
  227. def test_ticket47553_mode_default_add_deny(topology):
  228. '''
  229. This test case checks that the ADD operation fails (no ADD aci on production)
  230. '''
  231. topology.master1.log.info("\n\n######################### mode moddn_aci : ADD (should fail) ######################\n")
  232. _bind_normal(topology)
  233. #
  234. # First try to add an entry in production => INSUFFICIENT_ACCESS
  235. #
  236. try:
  237. topology.master1.log.info("Try to add %s" % PRODUCTION_DN)
  238. name = "%s%d" % (NEW_ACCOUNT, 0)
  239. topology.master1.add_s(Entry(("cn=%s,%s" % (name, PRODUCTION_DN), {
  240. 'objectclass': "top person".split(),
  241. 'sn': name,
  242. 'cn': name})))
  243. assert 0 # this is an error, we should not be allowed to add an entry in production
  244. except Exception as e:
  245. topology.master1.log.info("Exception (expected): %s" % type(e).__name__)
  246. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  247. def test_ticket47553_mode_default_ger_no_moddn(topology):
  248. topology.master1.log.info("\n\n######################### mode moddn_aci : GER no moddn ######################\n")
  249. request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn: " + BIND_DN)
  250. msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
  251. rtype, rdata, rmsgid, response_ctrl = topology.master1.result3(msg_id)
  252. #ger={}
  253. value = ''
  254. for dn, attrs in rdata:
  255. topology.master1.log.info("dn: %s" % dn)
  256. value = attrs['entryLevelRights'][0]
  257. topology.master1.log.info("############### entryLevelRights: %r" % value)
  258. assert 'n' not in value
  259. def test_ticket47553_mode_default_ger_with_moddn(topology):
  260. '''
  261. This test case adds the moddn aci and check ger contains 'n'
  262. '''
  263. topology.master1.log.info("\n\n######################### mode moddn_aci: GER with moddn ######################\n")
  264. # successfull MOD with the ACI
  265. _bind_manager(topology)
  266. _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN)
  267. _bind_normal(topology)
  268. request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn: " + BIND_DN)
  269. msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
  270. rtype, rdata, rmsgid, response_ctrl = topology.master1.result3(msg_id)
  271. #ger={}
  272. value = ''
  273. for dn, attrs in rdata:
  274. topology.master1.log.info("dn: %s" % dn)
  275. value = attrs['entryLevelRights'][0]
  276. topology.master1.log.info("############### entryLevelRights: %r" % value)
  277. assert 'n' in value
  278. # successfull MOD with the both ACI
  279. _bind_manager(topology)
  280. _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN)
  281. _bind_normal(topology)
  282. def test_ticket47553_mode_switch_default_to_legacy(topology):
  283. '''
  284. This test switch the server from default mode to legacy
  285. '''
  286. topology.master1.log.info("\n\n######################### Disable the moddn aci mod ######################\n")
  287. _bind_manager(topology)
  288. mod = [(ldap.MOD_REPLACE, CONFIG_MODDN_ACI_ATTR, 'off')]
  289. topology.master1.modify_s(DN_CONFIG, mod)
  290. def test_ticket47553_mode_legacy_ger_no_moddn1(topology):
  291. topology.master1.log.info("\n\n######################### mode legacy 1: GER no moddn ######################\n")
  292. request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn: " + BIND_DN)
  293. msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
  294. rtype, rdata, rmsgid, response_ctrl = topology.master1.result3(msg_id)
  295. #ger={}
  296. value = ''
  297. for dn, attrs in rdata:
  298. topology.master1.log.info("dn: %s" % dn)
  299. value = attrs['entryLevelRights'][0]
  300. topology.master1.log.info("############### entryLevelRights: %r" % value)
  301. assert 'n' not in value
  302. def test_ticket47553_mode_legacy_ger_no_moddn2(topology):
  303. topology.master1.log.info("\n\n######################### mode legacy 2: GER no moddn ######################\n")
  304. # successfull MOD with the ACI
  305. _bind_manager(topology)
  306. _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN)
  307. _bind_normal(topology)
  308. request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn: " + BIND_DN)
  309. msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
  310. rtype, rdata, rmsgid, response_ctrl = topology.master1.result3(msg_id)
  311. #ger={}
  312. value = ''
  313. for dn, attrs in rdata:
  314. topology.master1.log.info("dn: %s" % dn)
  315. value = attrs['entryLevelRights'][0]
  316. topology.master1.log.info("############### entryLevelRights: %r" % value)
  317. assert 'n' not in value
  318. # successfull MOD with the both ACI
  319. _bind_manager(topology)
  320. _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN)
  321. _bind_normal(topology)
  322. def test_ticket47553_mode_legacy_ger_with_moddn(topology):
  323. topology.master1.log.info("\n\n######################### mode legacy : GER with moddn ######################\n")
  324. # being allowed to read/write the RDN attribute use to allow the RDN
  325. ACI_TARGET = "(target = \"ldap:///%s\")(targetattr=\"cn\")" % (PRODUCTION_DN)
  326. ACI_ALLOW = "(version 3.0; acl \"MODDN production changing the RDN attribute\"; allow (read,search,write)"
  327. ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
  328. ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
  329. # successfull MOD with the ACI
  330. _bind_manager(topology)
  331. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  332. topology.master1.modify_s(SUFFIX, mod)
  333. _bind_normal(topology)
  334. request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn: " + BIND_DN)
  335. msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
  336. rtype, rdata, rmsgid, response_ctrl = topology.master1.result3(msg_id)
  337. #ger={}
  338. value = ''
  339. for dn, attrs in rdata:
  340. topology.master1.log.info("dn: %s" % dn)
  341. value = attrs['entryLevelRights'][0]
  342. topology.master1.log.info("############### entryLevelRights: %r" % value)
  343. assert 'n' in value
  344. # successfull MOD with the both ACI
  345. _bind_manager(topology)
  346. mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)]
  347. topology.master1.modify_s(SUFFIX, mod)
  348. _bind_normal(topology)
  349. def test_ticket47553_final(topology):
  350. topology.master1.delete()
  351. topology.master2.delete()
  352. log.info('Testcase PASSED')
  353. def run_isolated():
  354. '''
  355. run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
  356. To run isolated without py.test, you need to
  357. - edit this file and comment '@pytest.fixture' line before 'topology' function.
  358. - set the installation prefix
  359. - run this program
  360. '''
  361. global installation1_prefix
  362. global installation2_prefix
  363. installation1_prefix = None
  364. installation2_prefix = None
  365. topo = topology(True)
  366. topo.master1.log.info("\n\n######################### Ticket 47553 ######################\n")
  367. test_ticket47553_init(topo)
  368. # Check that without appropriate aci we are not allowed to add/delete
  369. test_ticket47553_mode_default_add_deny(topo)
  370. test_ticket47553_mode_default_ger_no_moddn(topo)
  371. test_ticket47553_mode_default_ger_with_moddn(topo)
  372. test_ticket47553_mode_switch_default_to_legacy(topo)
  373. test_ticket47553_mode_legacy_ger_no_moddn1(topo)
  374. test_ticket47553_mode_legacy_ger_no_moddn2(topo)
  375. test_ticket47553_mode_legacy_ger_with_moddn(topo)
  376. test_ticket47553_final(topo)
  377. if __name__ == '__main__':
  378. run_isolated()