ticket47553_ger.py 18 KB

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