ticket47653_test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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 logging
  10. import ldap
  11. import pytest
  12. from lib389 import Entry
  13. from lib389._constants import *
  14. from lib389.topologies import topology_st
  15. log = logging.getLogger(__name__)
  16. OC_NAME = 'OCticket47653'
  17. MUST = "(postalAddress $ postalCode)"
  18. MAY = "(member $ street)"
  19. OTHER_NAME = 'other_entry'
  20. MAX_OTHERS = 10
  21. BIND_NAME = 'bind_entry'
  22. BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX)
  23. BIND_PW = 'password'
  24. ENTRY_NAME = 'test_entry'
  25. ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX)
  26. ENTRY_OC = "top person %s" % OC_NAME
  27. def _oc_definition(oid_ext, name, must=None, may=None):
  28. oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
  29. desc = 'To test ticket 47490'
  30. sup = 'person'
  31. if not must:
  32. must = MUST
  33. if not may:
  34. may = MAY
  35. new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
  36. return new_oc
  37. def test_ticket47653_init(topology_st):
  38. """
  39. It adds
  40. - Objectclass with MAY 'member'
  41. - an entry ('bind_entry') with which we bind to test the 'SELFDN' operation
  42. It deletes the anonymous aci
  43. """
  44. topology_st.standalone.log.info("Add %s that allows 'member' attribute" % OC_NAME)
  45. new_oc = _oc_definition(2, OC_NAME, must=MUST, may=MAY)
  46. topology_st.standalone.schema.add_schema('objectClasses', new_oc)
  47. # entry used to bind with
  48. topology_st.standalone.log.info("Add %s" % BIND_DN)
  49. topology_st.standalone.add_s(Entry((BIND_DN, {
  50. 'objectclass': "top person".split(),
  51. 'sn': BIND_NAME,
  52. 'cn': BIND_NAME,
  53. 'userpassword': BIND_PW})))
  54. # enable acl error logging
  55. mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')]
  56. topology_st.standalone.modify_s(DN_CONFIG, mod)
  57. # Remove aci's to start with a clean slate
  58. mod = [(ldap.MOD_DELETE, 'aci', None)]
  59. topology_st.standalone.modify_s(SUFFIX, mod)
  60. # add dummy entries
  61. for cpt in range(MAX_OTHERS):
  62. name = "%s%d" % (OTHER_NAME, cpt)
  63. topology_st.standalone.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
  64. 'objectclass': "top person".split(),
  65. 'sn': name,
  66. 'cn': name})))
  67. def test_ticket47653_add(topology_st):
  68. '''
  69. It checks that, bound as bind_entry,
  70. - we can not ADD an entry without the proper SELFDN aci.
  71. - with the proper ACI we can not ADD with 'member' attribute
  72. - with the proper ACI and 'member' it succeeds to ADD
  73. '''
  74. topology_st.standalone.log.info("\n\n######################### ADD ######################\n")
  75. # bind as bind_entry
  76. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  77. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  78. # Prepare the entry with multivalued members
  79. entry_with_members = Entry(ENTRY_DN)
  80. entry_with_members.setValues('objectclass', 'top', 'person', 'OCticket47653')
  81. entry_with_members.setValues('sn', ENTRY_NAME)
  82. entry_with_members.setValues('cn', ENTRY_NAME)
  83. entry_with_members.setValues('postalAddress', 'here')
  84. entry_with_members.setValues('postalCode', '1234')
  85. members = []
  86. for cpt in range(MAX_OTHERS):
  87. name = "%s%d" % (OTHER_NAME, cpt)
  88. members.append("cn=%s,%s" % (name, SUFFIX))
  89. members.append(BIND_DN)
  90. entry_with_members.setValues('member', members)
  91. # Prepare the entry with one member
  92. entry_with_member = Entry(ENTRY_DN)
  93. entry_with_member.setValues('objectclass', 'top', 'person', 'OCticket47653')
  94. entry_with_member.setValues('sn', ENTRY_NAME)
  95. entry_with_member.setValues('cn', ENTRY_NAME)
  96. entry_with_member.setValues('postalAddress', 'here')
  97. entry_with_member.setValues('postalCode', '1234')
  98. member = []
  99. member.append(BIND_DN)
  100. entry_with_member.setValues('member', member)
  101. # entry to add WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
  102. try:
  103. topology_st.standalone.log.info("Try to add Add %s (aci is missing): %r" % (ENTRY_DN, entry_with_member))
  104. topology_st.standalone.add_s(entry_with_member)
  105. except Exception as e:
  106. topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
  107. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  108. # Ok Now add the proper ACI
  109. topology_st.standalone.log.info("Bind as %s and add the ADD SELFDN aci" % DN_DM)
  110. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  111. ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
  112. ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
  113. ACI_ALLOW = "(version 3.0; acl \"SelfDN add\"; allow (add)"
  114. ACI_SUBJECT = " userattr = \"member#selfDN\";)"
  115. ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
  116. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  117. topology_st.standalone.modify_s(SUFFIX, mod)
  118. # bind as bind_entry
  119. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  120. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  121. # entry to add WITHOUT member and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
  122. try:
  123. topology_st.standalone.log.info("Try to add Add %s (member is missing)" % ENTRY_DN)
  124. topology_st.standalone.add_s(Entry((ENTRY_DN, {
  125. 'objectclass': ENTRY_OC.split(),
  126. 'sn': ENTRY_NAME,
  127. 'cn': ENTRY_NAME,
  128. 'postalAddress': 'here',
  129. 'postalCode': '1234'})))
  130. except Exception as e:
  131. topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
  132. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  133. # entry to add WITH memberS and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
  134. # member should contain only one value
  135. try:
  136. topology_st.standalone.log.info("Try to add Add %s (with several member values)" % ENTRY_DN)
  137. topology_st.standalone.add_s(entry_with_members)
  138. except Exception as e:
  139. topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
  140. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  141. topology_st.standalone.log.info("Try to add Add %s should be successful" % ENTRY_DN)
  142. topology_st.standalone.add_s(entry_with_member)
  143. def test_ticket47653_search(topology_st):
  144. '''
  145. It checks that, bound as bind_entry,
  146. - we can not search an entry without the proper SELFDN aci.
  147. - adding the ACI, we can search the entry
  148. '''
  149. topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n")
  150. # bind as bind_entry
  151. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  152. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  153. # entry to search WITH member being BIND_DN but WITHOUT the ACI -> no entry returned
  154. topology_st.standalone.log.info("Try to search %s (aci is missing)" % ENTRY_DN)
  155. ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
  156. assert len(ents) == 0
  157. # Ok Now add the proper ACI
  158. topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
  159. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  160. ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
  161. ACI_TARGETATTR = "(targetattr = *)"
  162. ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
  163. ACI_ALLOW = "(version 3.0; acl \"SelfDN search-read\"; allow (read, search, compare)"
  164. ACI_SUBJECT = " userattr = \"member#selfDN\";)"
  165. ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
  166. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  167. topology_st.standalone.modify_s(SUFFIX, mod)
  168. # bind as bind_entry
  169. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  170. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  171. # entry to search with the proper aci
  172. topology_st.standalone.log.info("Try to search %s should be successful" % ENTRY_DN)
  173. ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
  174. assert len(ents) == 1
  175. def test_ticket47653_modify(topology_st):
  176. '''
  177. It checks that, bound as bind_entry,
  178. - we can not modify an entry without the proper SELFDN aci.
  179. - adding the ACI, we can modify the entry
  180. '''
  181. # bind as bind_entry
  182. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  183. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  184. topology_st.standalone.log.info("\n\n######################### MODIFY ######################\n")
  185. # entry to modify WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
  186. try:
  187. topology_st.standalone.log.info("Try to modify %s (aci is missing)" % ENTRY_DN)
  188. mod = [(ldap.MOD_REPLACE, 'postalCode', '9876')]
  189. topology_st.standalone.modify_s(ENTRY_DN, mod)
  190. except Exception as e:
  191. topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
  192. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  193. # Ok Now add the proper ACI
  194. topology_st.standalone.log.info("Bind as %s and add the WRITE SELFDN aci" % DN_DM)
  195. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  196. ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
  197. ACI_TARGETATTR = "(targetattr = *)"
  198. ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
  199. ACI_ALLOW = "(version 3.0; acl \"SelfDN write\"; allow (write)"
  200. ACI_SUBJECT = " userattr = \"member#selfDN\";)"
  201. ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
  202. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  203. topology_st.standalone.modify_s(SUFFIX, mod)
  204. # bind as bind_entry
  205. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  206. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  207. # modify the entry and checks the value
  208. topology_st.standalone.log.info("Try to modify %s. It should succeeds" % ENTRY_DN)
  209. mod = [(ldap.MOD_REPLACE, 'postalCode', '1928')]
  210. topology_st.standalone.modify_s(ENTRY_DN, mod)
  211. ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
  212. assert len(ents) == 1
  213. assert ents[0].postalCode == '1928'
  214. def test_ticket47653_delete(topology_st):
  215. '''
  216. It checks that, bound as bind_entry,
  217. - we can not delete an entry without the proper SELFDN aci.
  218. - adding the ACI, we can delete the entry
  219. '''
  220. topology_st.standalone.log.info("\n\n######################### DELETE ######################\n")
  221. # bind as bind_entry
  222. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  223. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  224. # entry to delete WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
  225. try:
  226. topology_st.standalone.log.info("Try to delete %s (aci is missing)" % ENTRY_DN)
  227. topology_st.standalone.delete_s(ENTRY_DN)
  228. except Exception as e:
  229. topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
  230. assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
  231. # Ok Now add the proper ACI
  232. topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
  233. topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
  234. ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
  235. ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
  236. ACI_ALLOW = "(version 3.0; acl \"SelfDN delete\"; allow (delete)"
  237. ACI_SUBJECT = " userattr = \"member#selfDN\";)"
  238. ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
  239. mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
  240. topology_st.standalone.modify_s(SUFFIX, mod)
  241. # bind as bind_entry
  242. topology_st.standalone.log.info("Bind as %s" % BIND_DN)
  243. topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
  244. # entry to search with the proper aci
  245. topology_st.standalone.log.info("Try to delete %s should be successful" % ENTRY_DN)
  246. topology_st.standalone.delete_s(ENTRY_DN)
  247. if __name__ == '__main__':
  248. # Run isolated
  249. # -s for DEBUG mode
  250. CURRENT_FILE = os.path.realpath(__file__)
  251. pytest.main("-s %s" % CURRENT_FILE)