Commit 60339ce Issue 49588 - Add py3 support for tickets : part-2

10 files Authored and Committed by aadhikari 2 months ago
Issue 49588 - Add py3 support for tickets : part-2

Description: Added py3 support by explicitly changing strings to bytes.
Ported tests from ticket to test suites, also added docstrings.

https://pagure.io/389-ds-base/issue/49588

Reviewed by: spichugi (Thanks!)

    
  1 @@ -0,0 +1,487 @@
  2 + # --- BEGIN COPYRIGHT BLOCK ---
  3 + # Copyright (C) 2016 Red Hat, Inc.
  4 + # All rights reserved.
  5 + #
  6 + # License: GPL (version 3 or any later version).
  7 + # See LICENSE for details.
  8 + # --- END COPYRIGHT BLOCK ---
  9 + #
 10 + from subprocess import Popen
 11 + 
 12 + import pytest
 13 + from lib389.paths import Paths
 14 + from lib389.tasks import *
 15 + from lib389.utils import *
 16 + from lib389.topologies import topology_st
 17 + 
 18 + from lib389._constants import DN_DM, DEFAULT_SUFFIX, PASSWORD, SERVERID_STANDALONE
 19 + 
 20 + logging.getLogger(__name__).setLevel(logging.DEBUG)
 21 + log = logging.getLogger(__name__)
 22 + 
 23 + CONFIG_DN = 'cn=config'
 24 + BOU = 'BOU'
 25 + BINDOU = 'ou=%s,%s' % (BOU, DEFAULT_SUFFIX)
 26 + BUID = 'buser123'
 27 + TUID = 'tuser0'
 28 + BINDDN = 'uid=%s,%s' % (BUID, BINDOU)
 29 + BINDPW = BUID
 30 + TESTDN = 'uid=%s,ou=people,%s' % (TUID, DEFAULT_SUFFIX)
 31 + TESTPW = TUID
 32 + BOGUSDN = 'uid=bogus,%s' % DEFAULT_SUFFIX
 33 + BOGUSDN2 = 'uid=bogus,ou=people,%s' % DEFAULT_SUFFIX
 34 + BOGUSSUFFIX = 'uid=bogus,ou=people,dc=bogus'
 35 + GROUPOU = 'ou=groups,%s' % DEFAULT_SUFFIX
 36 + BOGUSOU = 'ou=OU,%s' % DEFAULT_SUFFIX
 37 + 
 38 + def get_ldap_error_msg(e, type):
 39 +     return e.args[0][type]
 40 + 
 41 + def pattern_accesslog(file, log_pattern):
 42 +     for i in range(5):
 43 +         try:
 44 +             pattern_accesslog.last_pos += 1
 45 +         except AttributeError:
 46 +             pattern_accesslog.last_pos = 0
 47 + 
 48 +         found = None
 49 +         file.seek(pattern_accesslog.last_pos)
 50 + 
 51 +         # Use a while true iteration because 'for line in file: hit a
 52 +         # python bug that break file.tell()
 53 +         while True:
 54 +             line = file.readline()
 55 +             found = log_pattern.search(line)
 56 +             if ((line == '') or (found)):
 57 +                 break
 58 + 
 59 +         pattern_accesslog.last_pos = file.tell()
 60 +         if found:
 61 +             return line
 62 +         else:
 63 +             time.sleep(1)
 64 +     return None
 65 + 
 66 + 
 67 + def check_op_result(server, op, dn, superior, exists, rc):
 68 +     targetdn = dn
 69 +     if op == 'search':
 70 +         if exists:
 71 +             opstr = 'Searching existing entry'
 72 +         else:
 73 +             opstr = 'Searching non-existing entry'
 74 +     elif op == 'add':
 75 +         if exists:
 76 +             opstr = 'Adding existing entry'
 77 +         else:
 78 +             opstr = 'Adding non-existing entry'
 79 +     elif op == 'modify':
 80 +         if exists:
 81 +             opstr = 'Modifying existing entry'
 82 +         else:
 83 +             opstr = 'Modifying non-existing entry'
 84 +     elif op == 'modrdn':
 85 +         if superior is not None:
 86 +             targetdn = superior
 87 +             if exists:
 88 +                 opstr = 'Moving to existing superior'
 89 +             else:
 90 +                 opstr = 'Moving to non-existing superior'
 91 +         else:
 92 +             if exists:
 93 +                 opstr = 'Renaming existing entry'
 94 +             else:
 95 +                 opstr = 'Renaming non-existing entry'
 96 +     elif op == 'delete':
 97 +         if exists:
 98 +             opstr = 'Deleting existing entry'
 99 +         else:
100 +             opstr = 'Deleting non-existing entry'
101 + 
102 +     if ldap.SUCCESS == rc:
103 +         expstr = 'be ok'
104 +     else:
105 +         expstr = 'fail with %s' % rc.__name__
106 + 
107 +     log.info('%s %s, which should %s.' % (opstr, targetdn, expstr))
108 +     time.sleep(1)
109 +     hit = 0
110 +     try:
111 +         if op == 'search':
112 +             centry = server.search_s(dn, ldap.SCOPE_BASE, 'objectclass=*')
113 +         elif op == 'add':
114 +             server.add_s(Entry((dn, {'objectclass': 'top extensibleObject'.split(),
115 +                                      'cn': 'test entry'})))
116 +         elif op == 'modify':
117 +             server.modify_s(dn, [(ldap.MOD_REPLACE, 'description', b'test')])
118 +         elif op == 'modrdn':
119 +             if superior is not None:
120 +                 server.rename_s(dn, 'uid=new', newsuperior=superior, delold=1)
121 +             else:
122 +                 server.rename_s(dn, 'uid=new', delold=1)
123 +         elif op == 'delete':
124 +             server.delete_s(dn)
125 +         else:
126 +             log.fatal('Unknown operation %s' % op)
127 +             assert False
128 +     except ldap.LDAPError as e:
129 +         hit = 1
130 +         log.info("Exception (expected): %s" % type(e).__name__)
131 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
132 +         assert isinstance(e, rc)
133 +         if 'matched' in e.args:
134 +             log.info('Matched is returned: {}'.format(get_ldap_error_msg(e, 'matched')))
135 +             if rc != ldap.NO_SUCH_OBJECT:
136 +                 assert False
137 + 
138 +     if ldap.SUCCESS == rc:
139 +         if op == 'search':
140 +             log.info('Search should return none')
141 +             assert len(centry) == 0
142 +     else:
143 +         if 0 == hit:
144 +             log.info('Expected to fail with %s, but passed' % rc.__name__)
145 +             assert False
146 + 
147 +     log.info('PASSED\n')
148 + 
149 + 
150 + @pytest.mark.bz1347760
151 + def test_repeated_ldap_add(topology_st):
152 +     """Prevent revealing the entry info to whom has no access rights.
153 + 
154 +     :id: 76d278bd-3e51-4579-951a-753e6703b4df
155 +     :setup: Standalone instance
156 +     :steps:
157 +         1.  Disable accesslog logbuffering
158 +         2.  Bind as "cn=Directory Manager"
159 +         3.  Add a organisational unit as BOU
160 +         4.  Add a bind user as uid=buser123,ou=BOU,dc=example,dc=com
161 +         5.  Add a test user as uid=tuser0,ou=People,dc=example,dc=com
162 +         6.  Delete aci in dc=example,dc=com
163 +         7.  Bind as Directory Manager, acquire an access log path and instance dir
164 +         8.  Bind as uid=buser123,ou=BOU,dc=example,dc=com who has no right to read the entry
165 +         9.  Bind as uid=bogus,ou=people,dc=bogus,bogus who does not exist
166 +         10. Bind as uid=buser123,ou=BOU,dc=example,dc=com,bogus with wrong password
167 +         11. Adding aci for uid=buser123,ou=BOU,dc=example,dc=com to ou=BOU,dc=example,dc=com.
168 +         12. Bind as uid=buser123,ou=BOU,dc=example,dc=com now who has right to read the entry
169 +     :expectedresults:
170 +         1.  Operation should be successful
171 +         2.  Operation should be successful
172 +         3.  Operation should be successful
173 +         4.  Operation should be successful
174 +         5.  Operation should be successful
175 +         6.  Operation should be successful
176 +         7.  Operation should be successful
177 +         8.  Bind operation should be successful with no search result
178 +         9.  Bind operation should Fail
179 +         10. Bind operation should Fail
180 +         11. Operation should be successful
181 +         12. Bind operation should be successful with search result
182 +     """
183 +     log.info('Testing Bug 1347760 - Information disclosure via repeated use of LDAP ADD operation, etc.')
184 + 
185 +     log.info('Disabling accesslog logbuffering')
186 +     topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-accesslog-logbuffering', b'off')])
187 + 
188 +     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
189 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
190 + 
191 +     log.info('Adding ou=%s a bind user belongs to.' % BOU)
192 +     topology_st.standalone.add_s(Entry((BINDOU, {
193 +         'objectclass': 'top organizationalunit'.split(),
194 +         'ou': BOU})))
195 + 
196 +     log.info('Adding a bind user.')
197 +     topology_st.standalone.add_s(Entry((BINDDN,
198 +                                         {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
199 +                                          'cn': 'bind user',
200 +                                          'sn': 'user',
201 +                                          'userPassword': BINDPW})))
202 + 
203 +     log.info('Adding a test user.')
204 +     topology_st.standalone.add_s(Entry((TESTDN,
205 +                                         {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
206 +                                          'cn': 'test user',
207 +                                          'sn': 'user',
208 +                                          'userPassword': TESTPW})))
209 + 
210 +     log.info('Deleting aci in %s.' % DEFAULT_SUFFIX)
211 +     topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)])
212 + 
213 +     log.info('While binding as DM, acquire an access log path and instance dir')
214 +     ds_paths = Paths(serverid=topology_st.standalone.serverid,
215 +                      instance=topology_st.standalone)
216 +     file_path = ds_paths.access_log
217 +     inst_dir = ds_paths.inst_dir
218 + 
219 +     log.info('Bind case 1. the bind user has no rights to read the entry itself, bind should be successful.')
220 +     log.info('Bind as {%s,%s} who has no access rights.' % (BINDDN, BINDPW))
221 +     try:
222 +         topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
223 +     except ldap.LDAPError as e:
224 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
225 +         assert False
226 + 
227 +     file_obj = open(file_path, "r")
228 +     log.info('Access log path: %s' % file_path)
229 + 
230 +     log.info(
231 +         'Bind case 2-1. the bind user does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
232 +     log.info('Bind as {%s,%s} who does not exist.' % (BOGUSDN, 'bogus'))
233 +     try:
234 +         topology_st.standalone.simple_bind_s(BOGUSDN, 'bogus')
235 +     except ldap.LDAPError as e:
236 +         log.info("Exception (expected): %s" % type(e).__name__)
237 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
238 +         assert isinstance(e, ldap.INVALID_CREDENTIALS)
239 +         regex = re.compile('No such entry')
240 +         cause = pattern_accesslog(file_obj, regex)
241 +         if cause is None:
242 +             log.fatal('Cause not found - %s' % cause)
243 +             assert False
244 +         else:
245 +             log.info('Cause found - %s' % cause)
246 +     time.sleep(1)
247 + 
248 +     log.info(
249 +         'Bind case 2-2. the bind user\'s suffix does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
250 +     log.info('Bind as {%s,%s} who does not exist.' % (BOGUSSUFFIX, 'bogus'))
251 +     with pytest.raises(ldap.INVALID_CREDENTIALS):
252 +         topology_st.standalone.simple_bind_s(BOGUSSUFFIX, 'bogus')
253 +     regex = re.compile('No suffix for bind')
254 +     cause = pattern_accesslog(file_obj, regex)
255 +     if cause is None:
256 +         log.fatal('Cause not found - %s' % cause)
257 +         assert False
258 +     else:
259 +         log.info('Cause found - %s' % cause)
260 +     time.sleep(1)
261 + 
262 +     log.info(
263 +         'Bind case 2-3. the bind user\'s password is wrong, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
264 +     log.info('Bind as {%s,%s} who does not exist.' % (BINDDN, 'bogus'))
265 +     try:
266 +         topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
267 +     except ldap.LDAPError as e:
268 +         log.info("Exception (expected): %s" % type(e).__name__)
269 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
270 +         assert isinstance(e, ldap.INVALID_CREDENTIALS)
271 +         regex = re.compile('Invalid credentials')
272 +         cause = pattern_accesslog(file_obj, regex)
273 +         if cause is None:
274 +             log.fatal('Cause not found - %s' % cause)
275 +             assert False
276 +         else:
277 +             log.info('Cause found - %s' % cause)
278 +     time.sleep(1)
279 + 
280 +     log.info('Adding aci for %s to %s.' % (BINDDN, BINDOU))
281 +     acival = '(targetattr="*")(version 3.0; acl "%s"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
282 +     log.info('aci: %s' % acival)
283 +     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
284 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
285 +     topology_st.standalone.modify_s(BINDOU, [(ldap.MOD_ADD, 'aci', ensure_bytes(acival))])
286 +     time.sleep(1)
287 + 
288 +     log.info('Bind case 3. the bind user has the right to read the entry itself, bind should be successful.')
289 +     log.info('Bind as {%s,%s} which should be ok.\n' % (BINDDN, BINDPW))
290 +     topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
291 + 
292 +     log.info('The following operations are against the subtree the bind user %s has no rights.' % BINDDN)
293 +     # Search
294 +     exists = True
295 +     rc = ldap.SUCCESS
296 +     log.info(
297 +         'Search case 1. the bind user has no rights to read the search entry, it should return no search results with %s' % rc)
298 +     check_op_result(topology_st.standalone, 'search', TESTDN, None, exists, rc)
299 + 
300 +     exists = False
301 +     rc = ldap.SUCCESS
302 +     log.info(
303 +         'Search case 2-1. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
304 +     check_op_result(topology_st.standalone, 'search', BOGUSDN, None, exists, rc)
305 + 
306 +     exists = False
307 +     rc = ldap.SUCCESS
308 +     log.info(
309 +         'Search case 2-2. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
310 +     check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
311 + 
312 +     # Add
313 +     exists = True
314 +     rc = ldap.INSUFFICIENT_ACCESS
315 +     log.info(
316 +         'Add case 1. the bind user has no rights AND the adding entry exists, it should fail with %s' % rc.__name__)
317 +     check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
318 + 
319 +     exists = False
320 +     rc = ldap.INSUFFICIENT_ACCESS
321 +     log.info(
322 +         'Add case 2-1. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
323 +     check_op_result(topology_st.standalone, 'add', BOGUSDN, None, exists, rc)
324 + 
325 +     exists = False
326 +     rc = ldap.INSUFFICIENT_ACCESS
327 +     log.info(
328 +         'Add case 2-2. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
329 +     check_op_result(topology_st.standalone, 'add', BOGUSDN2, None, exists, rc)
330 + 
331 +     # Modify
332 +     exists = True
333 +     rc = ldap.INSUFFICIENT_ACCESS
334 +     log.info(
335 +         'Modify case 1. the bind user has no rights AND the modifying entry exists, it should fail with %s' % rc.__name__)
336 +     check_op_result(topology_st.standalone, 'modify', TESTDN, None, exists, rc)
337 + 
338 +     exists = False
339 +     rc = ldap.INSUFFICIENT_ACCESS
340 +     log.info(
341 +         'Modify case 2-1. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
342 +     check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
343 + 
344 +     exists = False
345 +     rc = ldap.INSUFFICIENT_ACCESS
346 +     log.info(
347 +         'Modify case 2-2. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
348 +     check_op_result(topology_st.standalone, 'modify', BOGUSDN2, None, exists, rc)
349 + 
350 +     # Modrdn
351 +     exists = True
352 +     rc = ldap.INSUFFICIENT_ACCESS
353 +     log.info(
354 +         'Modrdn case 1. the bind user has no rights AND the renaming entry exists, it should fail with %s' % rc.__name__)
355 +     check_op_result(topology_st.standalone, 'modrdn', TESTDN, None, exists, rc)
356 + 
357 +     exists = False
358 +     rc = ldap.INSUFFICIENT_ACCESS
359 +     log.info(
360 +         'Modrdn case 2-1. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
361 +     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
362 + 
363 +     exists = False
364 +     rc = ldap.INSUFFICIENT_ACCESS
365 +     log.info(
366 +         'Modrdn case 2-2. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
367 +     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN2, None, exists, rc)
368 + 
369 +     exists = True
370 +     rc = ldap.INSUFFICIENT_ACCESS
371 +     log.info(
372 +         'Modrdn case 3. the bind user has no rights AND the node moving an entry to exists, it should fail with %s' % rc.__name__)
373 +     check_op_result(topology_st.standalone, 'modrdn', TESTDN, GROUPOU, exists, rc)
374 + 
375 +     exists = False
376 +     rc = ldap.INSUFFICIENT_ACCESS
377 +     log.info(
378 +         'Modrdn case 4-1. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
379 +     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
380 + 
381 +     exists = False
382 +     rc = ldap.INSUFFICIENT_ACCESS
383 +     log.info(
384 +         'Modrdn case 4-2. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
385 +     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
386 + 
387 +     # Delete
388 +     exists = True
389 +     rc = ldap.INSUFFICIENT_ACCESS
390 +     log.info(
391 +         'Delete case 1. the bind user has no rights AND the deleting entry exists, it should fail with %s' % rc.__name__)
392 +     check_op_result(topology_st.standalone, 'delete', TESTDN, None, exists, rc)
393 + 
394 +     exists = False
395 +     rc = ldap.INSUFFICIENT_ACCESS
396 +     log.info(
397 +         'Delete case 2-1. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
398 +     check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
399 + 
400 +     exists = False
401 +     rc = ldap.INSUFFICIENT_ACCESS
402 +     log.info(
403 +         'Delete case 2-2. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
404 +     check_op_result(topology_st.standalone, 'delete', BOGUSDN2, None, exists, rc)
405 + 
406 +     log.info('EXTRA: Check no regressions')
407 +     log.info('Adding aci for %s to %s.' % (BINDDN, DEFAULT_SUFFIX))
408 +     acival = '(targetattr="*")(version 3.0; acl "%s-all"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
409 +     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
410 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
411 +     topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', ensure_bytes(acival))])
412 +     time.sleep(1)
413 + 
414 +     log.info('Bind as {%s,%s}.' % (BINDDN, BINDPW))
415 +     try:
416 +         topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
417 +     except ldap.LDAPError as e:
418 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
419 +         assert False
420 +     time.sleep(1)
421 + 
422 +     exists = False
423 +     rc = ldap.NO_SUCH_OBJECT
424 +     log.info('Search case. the search entry does not exist, the search should fail with %s' % rc.__name__)
425 +     check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
426 +     file_obj.close()
427 + 
428 +     exists = True
429 +     rc = ldap.ALREADY_EXISTS
430 +     log.info('Add case. the adding entry already exists, it should fail with %s' % rc.__name__)
431 +     check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
432 + 
433 +     exists = False
434 +     rc = ldap.NO_SUCH_OBJECT
435 +     log.info('Modify case. the modifying entry does not exist, it should fail with %s' % rc.__name__)
436 +     check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
437 + 
438 +     exists = False
439 +     rc = ldap.NO_SUCH_OBJECT
440 +     log.info('Modrdn case 1. the renaming entry does not exist, it should fail with %s' % rc.__name__)
441 +     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
442 + 
443 +     exists = False
444 +     rc = ldap.NO_SUCH_OBJECT
445 +     log.info('Modrdn case 2. the node moving an entry to does not, it should fail with %s' % rc.__name__)
446 +     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
447 + 
448 +     exists = False
449 +     rc = ldap.NO_SUCH_OBJECT
450 +     log.info('Delete case. the deleting entry does not exist, it should fail with %s' % rc.__name__)
451 +     check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
452 + 
453 +     log.info('Inactivate %s' % BINDDN)
454 +     if ds_paths.version < '1.3':
455 +         nsinactivate = '%s/ns-inactivate.pl' % inst_dir
456 +         nsinactivate_cmd = [nsinactivate, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN]
457 +     else:
458 +         nsinactivate = '%s/ns-inactivate.pl' % ds_paths.sbin_dir
459 +         nsinactivate_cmd = [nsinactivate, '-Z', SERVERID_STANDALONE, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN]
460 +     log.info(nsinactivate_cmd)
461 +     p = Popen(nsinactivate_cmd)
462 +     assert (p.wait() == 0)
463 + 
464 +     log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, BUID, ldap.UNWILLING_TO_PERFORM.__name__))
465 +     try:
466 +         topology_st.standalone.simple_bind_s(BINDDN, BUID)
467 +     except ldap.LDAPError as e:
468 +         log.info("Exception (expected): %s" % type(e).__name__)
469 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
470 +         assert isinstance(e, ldap.UNWILLING_TO_PERFORM)
471 + 
472 +     log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, 'bogus', ldap.UNWILLING_TO_PERFORM.__name__))
473 +     try:
474 +         topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
475 +     except ldap.LDAPError as e:
476 +         log.info("Exception (expected): %s" % type(e).__name__)
477 +         log.info('Desc {}'.format(get_ldap_error_msg(e,'desc')))
478 +         assert isinstance(e, ldap.UNWILLING_TO_PERFORM)
479 + 
480 +     log.info('SUCCESS')
481 + 
482 + 
483 + if __name__ == '__main__':
484 +     # Run isolated
485 +     # -s for DEBUG mode
486 +     CURRENT_FILE = os.path.realpath(__file__)
487 +     pytest.main("-s %s" % CURRENT_FILE)
488 + 
  1 @@ -0,0 +1,351 @@
  2 + # --- BEGIN COPYRIGHT BLOCK ---
  3 + # Copyright (C) 2016 Red Hat, Inc.
  4 + # All rights reserved.
  5 + #
  6 + # License: GPL (version 3 or any later version).
  7 + # See LICENSE for details.
  8 + # --- END COPYRIGHT BLOCK ---
  9 + #
 10 + import logging
 11 + 
 12 + import ldap
 13 + import pytest
 14 + from lib389 import Entry
 15 + from lib389._constants import *
 16 + from lib389.topologies import topology_st
 17 + 
 18 + log = logging.getLogger(__name__)
 19 + 
 20 + from lib389.utils import *
 21 + 
 22 + # Skip on older versions
 23 + pytestmark = pytest.mark.skipif(ds_is_older('1.3.2'), reason="Not implemented")
 24 + OC_NAME = 'OCticket47653'
 25 + MUST = "(postalAddress $ postalCode)"
 26 + MAY = "(member $ street)"
 27 + 
 28 + OTHER_NAME = 'other_entry'
 29 + MAX_OTHERS = 10
 30 + 
 31 + BIND_NAME = 'bind_entry'
 32 + BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX)
 33 + BIND_PW = 'password'
 34 + 
 35 + ENTRY_NAME = 'test_entry'
 36 + ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX)
 37 + ENTRY_OC = "top person %s" % OC_NAME
 38 + 
 39 + 
 40 + def _oc_definition(oid_ext, name, must=None, may=None):
 41 +     oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
 42 +     desc = 'To test ticket 47490'
 43 +     sup = 'person'
 44 +     if not must:
 45 +         must = MUST
 46 +     if not may:
 47 +         may = MAY
 48 + 
 49 +     new_oc = "( %s  NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
 50 +     return ensure_bytes(new_oc)
 51 + 
 52 + 
 53 + @pytest.fixture(scope="module")
 54 + def allow_user_init(topology_st):
 55 +     """Initialize the test environment
 56 + 
 57 +      """
 58 +     topology_st.standalone.log.info("Add %s that allows 'member' attribute" % OC_NAME)
 59 +     new_oc = _oc_definition(2, OC_NAME, must=MUST, may=MAY)
 60 +     topology_st.standalone.schema.add_schema('objectClasses', new_oc)
 61 + 
 62 +     # entry used to bind with
 63 +     topology_st.standalone.log.info("Add %s" % BIND_DN)
 64 +     topology_st.standalone.add_s(Entry((BIND_DN, {
 65 +         'objectclass': "top person".split(),
 66 +         'sn': BIND_NAME,
 67 +         'cn': BIND_NAME,
 68 +         'userpassword': BIND_PW})))
 69 + 
 70 +     # enable acl error logging
 71 +     mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'128')]
 72 +     topology_st.standalone.modify_s(DN_CONFIG, mod)
 73 + 
 74 +     # Remove aci's to start with a clean slate
 75 +     mod = [(ldap.MOD_DELETE, 'aci', None)]
 76 +     topology_st.standalone.modify_s(SUFFIX, mod)
 77 + 
 78 +     # add dummy entries
 79 +     for cpt in range(MAX_OTHERS):
 80 +         name = "%s%d" % (OTHER_NAME, cpt)
 81 +         topology_st.standalone.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
 82 +             'objectclass': "top person".split(),
 83 +             'sn': name,
 84 +             'cn': name})))
 85 + 
 86 + 
 87 + @pytest.mark.ds47653
 88 + def test_selfdn_permission_add(topology_st, allow_user_init):
 89 +     """Check add entry operation with and without SelfDN aci
 90 + 
 91 +     :id: e837a9ef-be92-48da-ad8b-ebf42b0fede1
 92 +     :setup: Standalone instance, add a entry which is used to bind,
 93 +     enable acl error logging by setting 'nsslapd-errorlog-level' to '128',
 94 +     remove aci's to start with a clean slate, and add dummy entries
 95 +     :steps:
 96 +         1. Check we can not ADD an entry without the proper SELFDN aci
 97 +         2. Check with the proper ACI we can not ADD with 'member' attribute
 98 +         3. Check entry to add with memberS and with the ACI
 99 +         4. Check with the proper ACI and 'member' it succeeds to ADD
100 +     :expectedresults:
101 +         1. Operation should be successful
102 +         2. Operation should be successful
103 +         3. Operation should fail with Insufficient Access
104 +         4. Operation should be successful
105 +      """
106 +     topology_st.standalone.log.info("\n\n######################### ADD ######################\n")
107 + 
108 +     # bind as bind_entry
109 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
110 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
111 + 
112 +     # Prepare the entry with multivalued members
113 +     entry_with_members = Entry(ENTRY_DN)
114 +     entry_with_members.setValues('objectclass', 'top', 'person', 'OCticket47653')
115 +     entry_with_members.setValues('sn', ENTRY_NAME)
116 +     entry_with_members.setValues('cn', ENTRY_NAME)
117 +     entry_with_members.setValues('postalAddress', 'here')
118 +     entry_with_members.setValues('postalCode', '1234')
119 +     members = []
120 +     for cpt in range(MAX_OTHERS):
121 +         name = "%s%d" % (OTHER_NAME, cpt)
122 +         members.append("cn=%s,%s" % (name, SUFFIX))
123 +     members.append(BIND_DN)
124 +     entry_with_members.setValues('member', members)
125 + 
126 +     # Prepare the entry with one member
127 +     entry_with_member = Entry(ENTRY_DN)
128 +     entry_with_member.setValues('objectclass', 'top', 'person', 'OCticket47653')
129 +     entry_with_member.setValues('sn', ENTRY_NAME)
130 +     entry_with_member.setValues('cn', ENTRY_NAME)
131 +     entry_with_member.setValues('postalAddress', 'here')
132 +     entry_with_member.setValues('postalCode', '1234')
133 +     member = []
134 +     member.append(BIND_DN)
135 +     entry_with_member.setValues('member', member)
136 + 
137 +     # entry to add WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
138 +     try:
139 +         topology_st.standalone.log.info("Try to add Add  %s (aci is missing): %r" % (ENTRY_DN, entry_with_member))
140 + 
141 +         topology_st.standalone.add_s(entry_with_member)
142 +     except Exception as e:
143 +         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
144 +         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
145 + 
146 +     # Ok Now add the proper ACI
147 +     topology_st.standalone.log.info("Bind as %s and add the ADD SELFDN aci" % DN_DM)
148 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
149 + 
150 +     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
151 +     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
152 +     ACI_ALLOW = "(version 3.0; acl \"SelfDN add\"; allow (add)"
153 +     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
154 +     ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
155 +     mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))]
156 +     topology_st.standalone.modify_s(SUFFIX, mod)
157 + 
158 +     # bind as bind_entry
159 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
160 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
161 + 
162 +     # entry to add WITHOUT member and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
163 +     try:
164 +         topology_st.standalone.log.info("Try to add Add  %s (member is missing)" % ENTRY_DN)
165 +         topology_st.standalone.add_s(Entry((ENTRY_DN, {
166 +             'objectclass': ENTRY_OC.split(),
167 +             'sn': ENTRY_NAME,
168 +             'cn': ENTRY_NAME,
169 +             'postalAddress': 'here',
170 +             'postalCode': '1234'})))
171 +     except Exception as e:
172 +         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
173 +         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
174 + 
175 +     # entry to add WITH memberS and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
176 +     # member should contain only one value
177 +     try:
178 +         topology_st.standalone.log.info("Try to add Add  %s (with several member values)" % ENTRY_DN)
179 +         topology_st.standalone.add_s(entry_with_members)
180 +     except Exception as e:
181 +         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
182 +         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
183 + 
184 +     topology_st.standalone.log.info("Try to add Add  %s should be successful" % ENTRY_DN)
185 +     topology_st.standalone.add_s(entry_with_member)
186 + 
187 + 
188 + @pytest.mark.ds47653
189 + def test_selfdn_permission_search(topology_st, allow_user_init):
190 +     """Check search operation with and without SelfDN aci
191 + 
192 +     :id: 06d51ef9-c675-4583-99b2-4852dbda190e
193 +     :setup: Standalone instance, add a entry which is used to bind,
194 +     enable acl error logging by setting 'nsslapd-errorlog-level' to '128',
195 +     remove aci's to start with a clean slate, and add dummy entries
196 +     :steps:
197 +         1. Check we can not search an entry without the proper SELFDN aci
198 +         2. Add proper ACI
199 +         3. Check we can search with the proper ACI
200 +     :expectedresults:
201 +         1. Operation should be successful
202 +         2. Operation should be successful
203 +         3. Operation should be successful
204 +      """
205 +     topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n")
206 +     # bind as bind_entry
207 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
208 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
209 + 
210 +     # entry to search WITH member being BIND_DN but WITHOUT the ACI -> no entry returned
211 +     topology_st.standalone.log.info("Try to search  %s (aci is missing)" % ENTRY_DN)
212 +     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
213 +     assert len(ents) == 0
214 + 
215 +     # Ok Now add the proper ACI
216 +     topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
217 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
218 + 
219 +     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
220 +     ACI_TARGETATTR = "(targetattr = *)"
221 +     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
222 +     ACI_ALLOW = "(version 3.0; acl \"SelfDN search-read\"; allow (read, search, compare)"
223 +     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
224 +     ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
225 +     mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))]
226 +     topology_st.standalone.modify_s(SUFFIX, mod)
227 + 
228 +     # bind as bind_entry
229 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
230 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
231 + 
232 +     # entry to search with the proper aci
233 +     topology_st.standalone.log.info("Try to search  %s should be successful" % ENTRY_DN)
234 +     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
235 +     assert len(ents) == 1
236 + 
237 + 
238 + @pytest.mark.ds47653
239 + def test_selfdn_permission_modify(topology_st, allow_user_init):
240 +     """Check modify operation with and without SelfDN aci
241 + 
242 +     :id: 97a58844-095f-44b0-9029-dd29a7d83d68
243 +     :setup: Standalone instance, add a entry which is used to bind,
244 +     enable acl error logging by setting 'nsslapd-errorlog-level' to '128',
245 +     remove aci's to start with a clean slate, and add dummy entries
246 +     :steps:
247 +         1. Check we can not modify an entry without the proper SELFDN aci
248 +         2. Add proper ACI
249 +         3. Modify the entry and check the modified value
250 +     :expectedresults:
251 +         1. Operation should be successful
252 +         2. Operation should be successful
253 +         3. Operation should be successful
254 +      """
255 +     # bind as bind_entry
256 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
257 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
258 + 
259 +     topology_st.standalone.log.info("\n\n######################### MODIFY ######################\n")
260 + 
261 +     # entry to modify WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
262 +     try:
263 +         topology_st.standalone.log.info("Try to modify  %s (aci is missing)" % ENTRY_DN)
264 +         mod = [(ldap.MOD_REPLACE, 'postalCode', b'9876')]
265 +         topology_st.standalone.modify_s(ENTRY_DN, mod)
266 +     except Exception as e:
267 +         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
268 +         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
269 + 
270 +     # Ok Now add the proper ACI
271 +     topology_st.standalone.log.info("Bind as %s and add the WRITE SELFDN aci" % DN_DM)
272 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
273 + 
274 +     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
275 +     ACI_TARGETATTR = "(targetattr = *)"
276 +     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
277 +     ACI_ALLOW = "(version 3.0; acl \"SelfDN write\"; allow (write)"
278 +     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
279 +     ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
280 +     mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))]
281 +     topology_st.standalone.modify_s(SUFFIX, mod)
282 + 
283 +     # bind as bind_entry
284 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
285 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
286 + 
287 +     # modify the entry and checks the value
288 +     topology_st.standalone.log.info("Try to modify  %s. It should succeeds" % ENTRY_DN)
289 +     mod = [(ldap.MOD_REPLACE, 'postalCode', b'1928')]
290 +     topology_st.standalone.modify_s(ENTRY_DN, mod)
291 + 
292 +     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
293 +     assert len(ents) == 1
294 +     assert ensure_str(ents[0].postalCode) == '1928'
295 + 
296 + 
297 + @pytest.mark.ds47653
298 + def test_selfdn_permission_delete(topology_st, allow_user_init):
299 +     """Check delete operation with and without SelfDN aci
300 + 
301 +     :id: 0ec4c0ec-e7b0-4ef1-8373-ab25aae34516
302 +     :setup: Standalone instance, add a entry which is used to bind,
303 +     enable acl error logging by setting 'nsslapd-errorlog-level' to '128',
304 +     remove aci's to start with a clean slate, and add dummy entries
305 +     :steps:
306 +         1. Check we can not delete an entry without the proper SELFDN aci
307 +         2. Add proper ACI
308 +         3. Check we can perform delete operation with proper ACI
309 +     :expectedresults:
310 +         1. Operation should be successful
311 +         2. Operation should be successful
312 +      """
313 +     topology_st.standalone.log.info("\n\n######################### DELETE ######################\n")
314 + 
315 +     # bind as bind_entry
316 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
317 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
318 + 
319 +     # entry to delete WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
320 +     try:
321 +         topology_st.standalone.log.info("Try to delete  %s (aci is missing)" % ENTRY_DN)
322 +         topology_st.standalone.delete_s(ENTRY_DN)
323 +     except Exception as e:
324 +         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
325 +         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
326 + 
327 +     # Ok Now add the proper ACI
328 +     topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
329 +     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
330 + 
331 +     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
332 +     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
333 +     ACI_ALLOW = "(version 3.0; acl \"SelfDN delete\"; allow (delete)"
334 +     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
335 +     ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
336 +     mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))]
337 +     topology_st.standalone.modify_s(SUFFIX, mod)
338 + 
339 +     # bind as bind_entry
340 +     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
341 +     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
342 + 
343 +     # entry to delete with the proper aci
344 +     topology_st.standalone.log.info("Try to delete  %s should be successful" % ENTRY_DN)
345 +     topology_st.standalone.delete_s(ENTRY_DN)
346 + 
347 + 
348 + if __name__ == '__main__':
349 +     # Run isolated
350 +     # -s for DEBUG mode
351 +     CURRENT_FILE = os.path.realpath(__file__)
352 +     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -0,0 +1,210 @@
  2 + # --- BEGIN COPYRIGHT BLOCK ---
  3 + # Copyright (C) 2016 Red Hat, Inc.
  4 + # All rights reserved.
  5 + #
  6 + # License: GPL (version 3 or any later version).
  7 + # See LICENSE for details.
  8 + # --- END COPYRIGHT BLOCK ---
  9 + #
 10 + import pytest
 11 + from lib389.tasks import *
 12 + from lib389.utils import *
 13 + from lib389.topologies import topology_st
 14 + 
 15 + from lib389._constants import DEFAULT_SUFFIX, DN_PLUGIN, SUFFIX, PLUGIN_7_BIT_CHECK
 16 + 
 17 + # Skip on older versions
 18 + pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented")
 19 + 
 20 + logging.getLogger(__name__).setLevel(logging.DEBUG)
 21 + log = logging.getLogger(__name__)
 22 + 
 23 + DN_7BITPLUGIN = "cn=7-bit check,%s" % DN_PLUGIN
 24 + ATTRS = ["uid", "mail", "userpassword", ",", SUFFIX, None]
 25 + 
 26 + 
 27 + @pytest.fixture(scope="module")
 28 + def enable_plugin(topology_st):
 29 +     """Enabling the 7-bit plugin for the
 30 +     environment setup"""
 31 +     log.info("Ticket 47431 - 0: Enable 7bit plugin...")
 32 +     topology_st.standalone.plugins.enable(name=PLUGIN_7_BIT_CHECK)
 33 + 
 34 + 
 35 + @pytest.mark.ds47431
 36 + def test_duplicate_values(topology_st, enable_plugin):
 37 +     """Check 26 duplicate values are treated as one
 38 + 
 39 +     :id: b23e04f1-2757-42cc-b3a2-26426c903f6d
 40 +     :setup: Standalone instance, enable 7bit plugin
 41 +     :steps:
 42 +         1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as :
 43 +            nsslapd-pluginarg0 : uid
 44 +            nsslapd-pluginarg1 : mail
 45 +            nsslapd-pluginarg2 : userpassword
 46 +            nsslapd-pluginarg3 : ,
 47 +            nsslapd-pluginarg4 : dc=example,dc=com
 48 +         2. Set nsslapd-pluginarg2 to 'userpassword' for multiple time (ideally 27)
 49 +         3. Check whether duplicate values are treated as one
 50 +     :expectedresults:
 51 +         1. It should be modified successfully
 52 +         2. It should be successful
 53 +         3. It should be successful
 54 +      """
 55 + 
 56 +     log.info("Ticket 47431 - 1: Check 26 duplicate values are treated as one...")
 57 +     expected = "str2entry_dupcheck.* duplicate values for attribute type nsslapd-pluginarg2 detected in entry cn=7-bit check,cn=plugins,cn=config."
 58 + 
 59 +     log.debug('modify_s %s' % DN_7BITPLUGIN)
 60 +     topology_st.standalone.modify_s(DN_7BITPLUGIN,
 61 +                                     [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', b"uid"),
 62 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"mail"),
 63 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', b"userpassword"),
 64 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', b","),
 65 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg4', ensure_bytes(SUFFIX))])
 66 + 
 67 +     arg2 = "nsslapd-pluginarg2: userpassword"
 68 +     topology_st.standalone.stop()
 69 +     dse_ldif = topology_st.standalone.confdir + '/dse.ldif'
 70 +     os.system('mv %s %s.47431' % (dse_ldif, dse_ldif))
 71 +     os.system(
 72 +         'sed -e "s/\\(%s\\)/\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1/" %s.47431 > %s' % (
 73 +         arg2, dse_ldif, dse_ldif))
 74 +     topology_st.standalone.start()
 75 + 
 76 +     cmdline = 'egrep -i "%s" %s' % (expected, topology_st.standalone.errlog)
 77 +     p = os.popen(cmdline, "r")
 78 +     line = p.readline()
 79 +     if line == "":
 80 +         log.error('Expected error "%s" not logged in %s' % (expected, topology_st.standalone.errlog))
 81 +         assert False
 82 +     else:
 83 +         log.debug('line: %s' % line)
 84 +         log.info('Expected error "%s" logged in %s' % (expected, topology_st.standalone.errlog))
 85 + 
 86 +     log.info("Ticket 47431 - 1: done")
 87 + 
 88 + 
 89 + @pytest.mark.ds47431
 90 + def test_multiple_value(topology_st, enable_plugin):
 91 +     """Check two values belonging to one arg is fixed
 92 + 
 93 +     :id: 20c802bc-332f-4e8d-bcfb-8cd28123d695
 94 +     :setup: Standalone instance, enable 7bit plugin
 95 +     :steps:
 96 +         1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as :
 97 +            nsslapd-pluginarg0 : uid
 98 +            nsslapd-pluginarg0 : mail
 99 +            nsslapd-pluginarg1 : userpassword
100 +            nsslapd-pluginarg2 : ,
101 +            nsslapd-pluginarg3 : dc=example,dc=com
102 +            nsslapd-pluginarg4 : None
103 +            (Note : While modifying add two attributes entries for nsslapd-pluginarg0)
104 + 
105 +         2. Check two values belonging to one arg is fixed
106 +     :expectedresults:
107 +         1. Entries should be modified successfully
108 +         2. Operation should be successful
109 +      """
110 + 
111 +     log.info("Ticket 47431 - 2: Check two values belonging to one arg is fixed...")
112 + 
113 +     topology_st.standalone.modify_s(DN_7BITPLUGIN,
114 +                                     [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', b"uid"),
115 +                                      (ldap.MOD_ADD, 'nsslapd-pluginarg0', b"mail"),
116 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"userpassword"),
117 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', b","),
118 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', ensure_bytes(SUFFIX)),
119 +                                      (ldap.MOD_DELETE, 'nsslapd-pluginarg4', None)])
120 + 
121 +     # PLUGIN LOG LEVEL
122 +     topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'65536')])
123 + 
124 +     topology_st.standalone.restart()
125 + 
126 +     cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog)
127 +     p = os.popen(cmdline, "r")
128 +     i = 0
129 +     while ATTRS[i]:
130 +         line = p.readline()
131 +         log.debug('line - %s' % line)
132 +         log.debug('ATTRS[%d] %s' % (i, ATTRS[i]))
133 +         if line == "":
134 +             break
135 +         elif line.find(ATTRS[i]) >= 0:
136 +             log.debug('%s was logged' % ATTRS[i])
137 +         else:
138 +             log.error('%s was not logged.' % ATTRS[i])
139 +             assert False
140 +         i = i + 1
141 + 
142 +     log.info("Ticket 47431 - 2: done")
143 + 
144 + 
145 + @pytest.mark.ds47431
146 + def test_missing_args(topology_st, enable_plugin):
147 +     """Check missing args are fixed
148 + 
149 +     :id: b2814399-7ed2-4fe0-981d-b0bdbbe31cfb
150 +     :setup: Standalone instance, enable 7bit plugin
151 +     :steps:
152 +         1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as :
153 +            nsslapd-pluginarg0 : None
154 +            nsslapd-pluginarg1 : uid
155 +            nsslapd-pluginarg2 : None
156 +            nsslapd-pluginarg3 : mail
157 +            nsslapd-pluginarg5 : userpassword
158 +            nsslapd-pluginarg7 : ,
159 +            nsslapd-pluginarg9 : dc=example,dc=com
160 +            (Note: While modifying add 2 entries as None)
161 + 
162 +         2. Change the nsslapd-errorlog-level to 65536
163 +         3. Check missing agrs are fixed
164 +     :expectedresults:
165 +         1. Entries should be modified successfully
166 +         2. Operation should be successful
167 +         3. Operation should be successful
168 +      """
169 + 
170 +     log.info("Ticket 47431 - 3: Check missing args are fixed...")
171 + 
172 +     topology_st.standalone.modify_s(DN_7BITPLUGIN,
173 +                                     [(ldap.MOD_DELETE, 'nsslapd-pluginarg0', None),
174 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"uid"),
175 +                                      (ldap.MOD_DELETE, 'nsslapd-pluginarg2', None),
176 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', b"mail"),
177 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg5', b"userpassword"),
178 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg7', b","),
179 +                                      (ldap.MOD_REPLACE, 'nsslapd-pluginarg9', ensure_bytes(SUFFIX))])
180 + 
181 +     # PLUGIN LOG LEVEL
182 +     topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'65536')])
183 + 
184 +     topology_st.standalone.stop()
185 +     os.system('mv %s %s.47431' % (topology_st.standalone.errlog, topology_st.standalone.errlog))
186 +     os.system('touch %s' % (topology_st.standalone.errlog))
187 +     topology_st.standalone.start()
188 + 
189 +     cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog)
190 +     p = os.popen(cmdline, "r")
191 +     i = 0
192 +     while ATTRS[i]:
193 +         line = p.readline()
194 +         if line == "":
195 +             break
196 +         elif line.find(ATTRS[i]) >= 0:
197 +             log.debug('%s was logged' % ATTRS[i])
198 +         else:
199 +             log.error('%s was not logged.' % ATTRS[i])
200 +             assert False
201 +         i = i + 1
202 + 
203 +     log.info("Ticket 47431 - 3: done")
204 +     log.info('Test complete')
205 + 
206 + 
207 + if __name__ == '__main__':
208 +     # Run isolated
209 +     # -s for DEBUG mode
210 +     CURRENT_FILE = os.path.realpath(__file__)
211 +     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -16,9 +16,17 @@
  2   from lib389.idm.user import UserAccounts
  3   from lib389.topologies import topology_m2 as topo
  4   from lib389._constants import *
  5 + from lib389.tasks import *
  6 + from lib389.utils import *
  7   
  8   TEST_ENTRY_NAME = 'replusr'
  9   NEW_RDN_NAME = 'cl5usr'
 10 + CHANGELOG = 'cn=changelog5,cn=config'
 11 + RETROCHANGELOG = 'cn=Retro Changelog Plugin,cn=plugins,cn=config'
 12 + MAXAGE = 'nsslapd-changelogmaxage'
 13 + TRIMINTERVAL = 'nsslapd-changelogtrim-interval'
 14 + COMPACTDBINTERVAL = 'nsslapd-changelogcompactdb-interval'
 15 + FILTER = '(cn=*)'
 16   
 17   DEBUGGING = os.getenv('DEBUGGING', default=False)
 18   if DEBUGGING:
 19 @@ -109,6 +117,84 @@
 20               changetype operations'
 21   
 22   
 23 + def get_ldap_error_msg(e, type):
 24 +     return e.args[0][type]
 25 + 
 26 + 
 27 + @pytest.fixture(scope="module")
 28 + def changelog_init(topo):
 29 +     """Initialize the test environment by changing log dir and
 30 +     enabling cn=Retro Changelog Plugin,cn=plugins,cn=config
 31 +      """
 32 +     log.info('Testing Ticket 47669 - Test duration syntax in the changelogs')
 33 + 
 34 +     # bind as directory manager
 35 +     topo.ms["master1"].log.info("Bind as %s" % DN_DM)
 36 +     topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD)
 37 + 
 38 +     try:
 39 +         changelogdir = os.path.join(os.path.dirname(topo.ms["master1"].dbdir), 'changelog')
 40 +         topo.ms["master1"].modify_s(CHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-changelogdir',
 41 +                                                                     ensure_bytes(changelogdir))])
 42 +     except ldap.LDAPError as e:
 43 +         log.error('Failed to modify ' + CHANGELOG + ': error {}'.format(get_ldap_error_msg(e,'desc')))
 44 +         assert False
 45 + 
 46 +     try:
 47 +         topo.ms["master1"].modify_s(RETROCHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', b'on')])
 48 +     except ldap.LDAPError as e:
 49 +         log.error('Failed to enable ' + RETROCHANGELOG + ': error {}'.format(get_ldap_error_msg(e, 'desc')))
 50 +         assert False
 51 + 
 52 +     # restart the server
 53 +     topo.ms["master1"].restart(timeout=10)
 54 + 
 55 + 
 56 + def add_and_check(topo, plugin, attr, val, isvalid):
 57 +     """
 58 +     Helper function to add/replace attr: val and check the added value
 59 +     """
 60 +     if isvalid:
 61 +         log.info('Test %s: %s -- valid' % (attr, val))
 62 +         try:
 63 +             topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))])
 64 +         except ldap.LDAPError as e:
 65 +             log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc')))
 66 +             assert False
 67 +     else:
 68 +         log.info('Test %s: %s -- invalid' % (attr, val))
 69 +         if plugin == CHANGELOG:
 70 +             try:
 71 +                 topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))])
 72 +             except ldap.LDAPError as e:
 73 +                 log.error('Expectedly failed to add ' + attr + ': ' + val +
 74 +                           ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc')))
 75 +         else:
 76 +             try:
 77 +                 topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))])
 78 +             except ldap.LDAPError as e:
 79 +                 log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc')))
 80 + 
 81 +     try:
 82 +         entries = topo.ms["master1"].search_s(plugin, ldap.SCOPE_BASE, FILTER, [attr])
 83 +         if isvalid:
 84 +             if not entries[0].hasValue(attr, val):
 85 +                 log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val))
 86 +                 assert False
 87 +         else:
 88 +             if plugin == CHANGELOG:
 89 +                 if entries[0].hasValue(attr, val):
 90 +                     log.fatal('%s has unexpected (%s: %s)' % (plugin, attr, val))
 91 +                     assert False
 92 +             else:
 93 +                 if not entries[0].hasValue(attr, val):
 94 +                     log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val))
 95 +                     assert False
 96 +     except ldap.LDAPError as e:
 97 +         log.fatal('Unable to search for entry %s: error %s' % (plugin, e.message['desc']))
 98 +         assert False
 99 + 
100 + 
101   def test_verify_changelog(topo):
102       """Check if changelog dump file contains required ldap operations
103   
104 @@ -230,8 +316,143 @@
105       _check_changelog_ldif(topo, changelog_ldif)
106   
107   
108 + @pytest.mark.ds47669
109 + def test_changelog_maxage(topo, changelog_init):
110 +     """Check nsslapd-changelog max age values
111 + 
112 +     :id: d284ff27-03b2-412c-ac74-ac4f2d2fae3b
113 +     :setup: Replication with two master, change nsslapd-changelogdir to
114 +     '/var/lib/dirsrv/slapd-master1/changelog' and
115 +     set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on'
116 +     :steps:
117 +         1. Set nsslapd-changelogmaxage in cn=changelog5,cn=config to values - '12345','10s','30M','12h','2D','4w'
118 +         2. Set nsslapd-changelogmaxage in cn=changelog5,cn=config to values - '-123','xyz'
119 + 
120 +     :expectedresults:
121 +         1. Operation should be successful
122 +         2. Operation should be unsuccessful
123 +      """
124 +     log.info('1. Test nsslapd-changelogmaxage in cn=changelog5,cn=config')
125 + 
126 +     # bind as directory manager
127 +     topo.ms["master1"].log.info("Bind as %s" % DN_DM)
128 +     topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD)
129 + 
130 +     add_and_check(topo, CHANGELOG, MAXAGE, '12345', True)
131 +     add_and_check(topo, CHANGELOG, MAXAGE, '10s', True)
132 +     add_and_check(topo, CHANGELOG, MAXAGE, '30M', True)
133 +     add_and_check(topo, CHANGELOG, MAXAGE, '12h', True)
134 +     add_and_check(topo, CHANGELOG, MAXAGE, '2D', True)
135 +     add_and_check(topo, CHANGELOG, MAXAGE, '4w', True)
136 +     add_and_check(topo, CHANGELOG, MAXAGE, '-123', False)
137 +     add_and_check(topo, CHANGELOG, MAXAGE, 'xyz', False)
138 + 
139 + 
140 + @pytest.mark.ds47669
141 + def test_ticket47669_changelog_triminterval(topo, changelog_init):
142 +     """Check nsslapd-changelog triminterval values
143 + 
144 +     :id: 8f850c37-7e7c-49dd-a4e0-9344638616d6
145 +     :setup: Replication with two master, change nsslapd-changelogdir to
146 +     '/var/lib/dirsrv/slapd-master1/changelog' and
147 +     set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on'
148 +     :steps:
149 +         1. Set nsslapd-changelogtrim-interval in cn=changelog5,cn=config to values -
150 +            '12345','10s','30M','12h','2D','4w'
151 +         2. Set nsslapd-changelogtrim-interval in cn=changelog5,cn=config to values - '-123','xyz'
152 + 
153 +     :expectedresults:
154 +         1. Operation should be successful
155 +         2. Operation should be unsuccessful
156 +      """
157 +     log.info('2. Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config')
158 + 
159 +     # bind as directory manager
160 +     topo.ms["master1"].log.info("Bind as %s" % DN_DM)
161 +     topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD)
162 + 
163 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '12345', True)
164 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '10s', True)
165 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '30M', True)
166 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '12h', True)
167 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '2D', True)
168 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '4w', True)
169 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, '-123', False)
170 +     add_and_check(topo, CHANGELOG, TRIMINTERVAL, 'xyz', False)
171 + 
172 + 
173 + @pytest.mark.ds47669
174 + def test_changelog_compactdbinterval(topo, changelog_init):
175 +     """Check nsslapd-changelog compactdbinterval values
176 + 
177 +     :id: 0f4b3118-9dfa-4c2a-945c-72847b42a48c
178 +     :setup: Replication with two master, change nsslapd-changelogdir to
179 +     '/var/lib/dirsrv/slapd-master1/changelog' and
180 +     set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on'
181 +     :steps:
182 +         1. Set nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config to values -
183 +            '12345','10s','30M','12h','2D','4w'
184 +         2. Set nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config to values -
185 +            '-123','xyz'
186 + 
187 +     :expectedresults:
188 +         1. Operation should be successful
189 +         2. Operation should be unsuccessful
190 +      """
191 +     log.info('3. Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config')
192 + 
193 +     # bind as directory manager
194 +     topo.ms["master1"].log.info("Bind as %s" % DN_DM)
195 +     topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD)
196 + 
197 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '12345', True)
198 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '10s', True)
199 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '30M', True)
200 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '12h', True)
201 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '2D', True)
202 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '4w', True)
203 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '-123', False)
204 +     add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, 'xyz', False)
205 + 
206 + 
207 + @pytest.mark.ds47669
208 + def test_retrochangelog_maxage(topo, changelog_init):
209 +     """Check nsslapd-retrochangelog max age values
210 + 
211 +     :id: 0cb84d81-3e86-4dbf-84a2-66aefd8281db
212 +     :setup: Replication with two master, change nsslapd-changelogdir to
213 +     '/var/lib/dirsrv/slapd-master1/changelog' and
214 +     set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on'
215 +     :steps:
216 +         1. Set nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config to values -
217 +            '12345','10s','30M','12h','2D','4w'
218 +         2. Set nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config to values -
219 +            '-123','xyz'
220 + 
221 +     :expectedresults:
222 +         1. Operation should be successful
223 +         2. Operation should be unsuccessful
224 +      """
225 +     log.info('4. Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config')
226 + 
227 +     # bind as directory manager
228 +     topo.ms["master1"].log.info("Bind as %s" % DN_DM)
229 +     topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD)
230 + 
231 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '12345', True)
232 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '10s', True)
233 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '30M', True)
234 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '12h', True)
235 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '2D', True)
236 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '4w', True)
237 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, '-123', False)
238 +     add_and_check(topo, RETROCHANGELOG, MAXAGE, 'xyz', False)
239 + 
240 +     topo.ms["master1"].log.info("ticket47669 was successfully verified.")
241 + 
242 + 
243   if __name__ == '__main__':
244       # Run isolated
245       # -s for DEBUG mode
246       CURRENT_FILE = os.path.realpath(__file__)
247 -     pytest.main('-s {}'.format(CURRENT_FILE))
248 +     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -0,0 +1,701 @@
  2 + # --- BEGIN COPYRIGHT BLOCK ---
  3 + # Copyright (C) 2016 Red Hat, Inc.
  4 + # All rights reserved.
  5 + #
  6 + # License: GPL (version 3 or any later version).
  7 + # See LICENSE for details.
  8 + # --- END COPYRIGHT BLOCK ---
  9 + #
 10 + '''
 11 + Created on Nov 7, 2013
 12 + 
 13 + @author: tbordaz
 14 + '''
 15 + import logging
 16 + import re
 17 + import time
 18 + 
 19 + import ldap
 20 + import pytest
 21 + from lib389 import Entry
 22 + from lib389._constants import *
 23 + from lib389.topologies import topology_m1c1
 24 + 
 25 + from lib389.utils import *
 26 + 
 27 + # Skip on older versions
 28 + pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented")
 29 + logging.getLogger(__name__).setLevel(logging.DEBUG)
 30 + log = logging.getLogger(__name__)
 31 + 
 32 + TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
 33 + ENTRY_DN = "cn=test_entry, %s" % SUFFIX
 34 + MUST_OLD = "(postalAddress $ preferredLocale)"
 35 + MUST_NEW = "(postalAddress $ preferredLocale $ telexNumber)"
 36 + MAY_OLD = "(postalCode $ street)"
 37 + MAY_NEW = "(postalCode $ street $ postOfficeBox)"
 38 + 
 39 + 
 40 + def _header(topology_m1c1, label):
 41 +     topology_m1c1.ms["master1"].log.info("\n\n###############################################")
 42 +     topology_m1c1.ms["master1"].log.info("#######")
 43 +     topology_m1c1.ms["master1"].log.info("####### %s" % label)
 44 +     topology_m1c1.ms["master1"].log.info("#######")
 45 +     topology_m1c1.ms["master1"].log.info("###################################################")
 46 + 
 47 + 
 48 + def pattern_errorlog(file, log_pattern):
 49 +     try:
 50 +         pattern_errorlog.last_pos += 1
 51 +     except AttributeError:
 52 +         pattern_errorlog.last_pos = 0
 53 + 
 54 +     found = None
 55 +     log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos)
 56 +     file.seek(pattern_errorlog.last_pos)
 57 + 
 58 +     # Use a while true iteration because 'for line in file: hit a
 59 +     # python bug that break file.tell()
 60 +     while True:
 61 +         line = file.readline()
 62 +         log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line))
 63 +         found = log_pattern.search(line)
 64 +         if ((line == '') or (found)):
 65 +             break
 66 + 
 67 +     log.debug("_pattern_errorlog: end at offset %d" % file.tell())
 68 +     pattern_errorlog.last_pos = file.tell()
 69 +     return found
 70 + 
 71 + 
 72 + def _oc_definition(oid_ext, name, must=None, may=None):
 73 +     oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
 74 +     desc = 'To test ticket 47490'
 75 +     sup = 'person'
 76 +     if not must:
 77 +         must = MUST_OLD
 78 +     if not may:
 79 +         may = MAY_OLD
 80 + 
 81 +     new_oc = "( %s  NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
 82 +     return new_oc
 83 + 
 84 + 
 85 + def add_OC(instance, oid_ext, name):
 86 +     new_oc = _oc_definition(oid_ext, name)
 87 +     instance.schema.add_schema('objectClasses', ensure_bytes(new_oc))
 88 + 
 89 + 
 90 + def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None):
 91 +     old_oc = _oc_definition(oid_ext, name, old_must, old_may)
 92 +     new_oc = _oc_definition(oid_ext, name, new_must, new_may)
 93 +     instance.schema.del_schema('objectClasses', ensure_bytes(old_oc))
 94 +     instance.schema.add_schema('objectClasses', ensure_bytes(new_oc))
 95 + 
 96 + 
 97 + def support_schema_learning(topology_m1c1):
 98 +     """
 99 +     with https://fedorahosted.org/389/ticket/47721, the supplier and consumer can learn
100 +     schema definitions when a replication occurs.
101 +     Before that ticket: replication of the schema fails requiring administrative operation
102 +     In the test the schemaCSN (master consumer) differs
103 + 
104 +     After that ticket: replication of the schema succeeds (after an initial phase of learning)
105 +     In the test the schema CSN (master consumer) are in sync
106 + 
107 +     This function returns True if 47721 is fixed in the current release
108 +     False else
109 +     """
110 +     ent = topology_m1c1.cs["consumer1"].getEntry(DN_CONFIG, ldap.SCOPE_BASE, "(cn=config)", ['nsslapd-versionstring'])
111 +     if ent.hasAttr('nsslapd-versionstring'):
112 +         val = ent.getValue('nsslapd-versionstring')
113 +         version = ensure_str(val).split('/')[1].split('.')  # something like ['1', '3', '1', '23', 'final_fix']
114 +         major = int(version[0])
115 +         minor = int(version[1])
116 +         if major > 1:
117 +             return True
118 +         if minor > 3:
119 +             # version is 1.4 or after
120 +             return True
121 +         if minor == 3:
122 +             if version[2].isdigit():
123 +                 if int(version[2]) >= 3:
124 +                     return True
125 +         return False
126 + 
127 + 
128 + def trigger_update(topology_m1c1):
129 +     """
130 +         It triggers an update on the supplier. This will start a replication
131 +         session and a schema push
132 +     """
133 +     try:
134 +         trigger_update.value += 1
135 +     except AttributeError:
136 +         trigger_update.value = 1
137 +     replace = [(ldap.MOD_REPLACE, 'telephonenumber', ensure_bytes(str(trigger_update.value)))]
138 +     topology_m1c1.ms["master1"].modify_s(ENTRY_DN, replace)
139 + 
140 +     # wait 10 seconds that the update is replicated
141 +     loop = 0
142 +     while loop <= 10:
143 +         try:
144 +             ent = topology_m1c1.cs["consumer1"].getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)",
145 +                                                          ['telephonenumber'])
146 +             val = ent.telephonenumber or "0"
147 +             if int(val) == trigger_update.value:
148 +                 return
149 +             # the expected value is not yet replicated. try again
150 +             time.sleep(1)
151 +             loop += 1
152 +             log.debug("trigger_update: receive %s (expected %d)" % (val, trigger_update.value))
153 +         except ldap.NO_SUCH_OBJECT:
154 +             time.sleep(1)
155 +             loop += 1
156 + 
157 + 
158 + def trigger_schema_push(topology_m1c1):
159 +     '''
160 +     Trigger update to create a replication session.
161 +     In case of 47721 is fixed and the replica needs to learn the missing definition, then
162 +     the first replication session learn the definition and the second replication session
163 +     push the schema (and the schemaCSN.
164 +     This is why there is two updates and replica agreement is stopped/start (to create a second session)
165 +     '''
166 +     agreements = topology_m1c1.ms["master1"].agreement.list(suffix=SUFFIX,
167 +                                                             consumer_host=topology_m1c1.cs["consumer1"].host,
168 +                                                             consumer_port=topology_m1c1.cs["consumer1"].port)
169 +     assert (len(agreements) == 1)
170 +     ra = agreements[0]
171 +     trigger_update(topology_m1c1)
172 +     topology_m1c1.ms["master1"].agreement.pause(ra.dn)
173 +     topology_m1c1.ms["master1"].agreement.resume(ra.dn)
174 +     trigger_update(topology_m1c1)
175 + 
176 + 
177 + @pytest.fixture(scope="module")
178 + def schema_replication_init(topology_m1c1):
179 +     """Initialize the test environment
180 + 
181 +     """
182 +     log.debug("test_schema_replication_init topology_m1c1 %r (master %r, consumer %r" % (
183 +     topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
184 +     # check if a warning message is logged in the
185 +     # error log of the supplier
186 +     topology_m1c1.ms["master1"].errorlog_file = open(topology_m1c1.ms["master1"].errlog, "r")
187 + 
188 +     # This entry will be used to trigger attempt of schema push
189 +     topology_m1c1.ms["master1"].add_s(Entry((ENTRY_DN, {
190 +         'objectclass': "top person".split(),
191 +         'sn': 'test_entry',
192 +         'cn': 'test_entry'})))
193 + 
194 + 
195 + @pytest.mark.ds47490
196 + def test_schema_replication_one(topology_m1c1, schema_replication_init):
197 +     """Check supplier schema is a superset (one extra OC) of consumer schema, then
198 +     schema is pushed and there is no message in the error log
199 + 
200 +     :id: d6c6ff30-b3ae-4001-80ff-0fb18563a393
201 +     :setup: Master Consumer, check if a warning message is logged in the
202 +     error log of the supplier and add a test entry to trigger attempt of schema push.
203 +     :steps:
204 +         1. Update the schema of supplier, so it will be superset of consumer
205 +         2. Push the Schema (no error)
206 +         3. Check both master and consumer has same schemaCSN
207 +         4. Check the startup/final state
208 +     :expectedresults:
209 +         1. Operation should be successful
210 +         2. Operation should be successful
211 +         3. Operation should be successful
212 +         4. State at startup:
213 +             - supplier default schema
214 +             - consumer default schema
215 +            Final state
216 +             - supplier +masterNewOCA
217 +             - consumer +masterNewOCA
218 +     """
219 + 
220 +     _header(topology_m1c1, "Extra OC Schema is pushed - no error")
221 + 
222 +     log.debug("test_schema_replication_one topology_m1c1 %r (master %r, consumer %r" % (
223 +     topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
224 +     # update the schema of the supplier so that it is a superset of
225 +     # consumer. Schema should be pushed
226 +     add_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA')
227 + 
228 +     trigger_schema_push(topology_m1c1)
229 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
230 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
231 + 
232 +     # Check the schemaCSN was updated on the consumer
233 +     log.debug("test_schema_replication_one master_schema_csn=%s", master_schema_csn)
234 +     log.debug("ctest_schema_replication_one onsumer_schema_csn=%s", consumer_schema_csn)
235 +     assert master_schema_csn == consumer_schema_csn
236 + 
237 +     # Check the error log of the supplier does not contain an error
238 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
239 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
240 +     if res is not None:
241 +         assert False
242 + 
243 + 
244 + @pytest.mark.ds47490
245 + def test_schema_replication_two(topology_m1c1, schema_replication_init):
246 +     """Check consumer schema is a superset (one extra OC) of supplier schema, then
247 +         schema is pushed and there is a message in the error log
248 + 
249 +     :id: b5db9b75-a9a7-458e-86ec-2a8e7bd1c014
250 +     :setup: Master Consumer, check if a warning message is logged in the
251 +     error log of the supplier and add a test entry to trigger attempt of schema push.
252 +     :steps:
253 +         1. Update the schema of consumer, so it will be superset of supplier
254 +         2. Update the schema of supplier so ti make it's nsSchemaCSN larger than consumer
255 +         3. Push the Schema (error should be generated)
256 +         4. Check supplier learns the missing definition
257 +         5. Check the error logs
258 +         6. Check the startup/final state
259 +     :expectedresults:
260 +         1. Operation should be successful
261 +         2. Operation should be successful
262 +         3. Operation should be successful
263 +         4. Operation should be successful
264 +         5. Operation should be successful
265 +         6. State at startup
266 +             - supplier +masterNewOCA
267 +             - consumer +masterNewOCA
268 +            Final state
269 +             - supplier +masterNewOCA +masterNewOCB
270 +             - consumer +masterNewOCA               +consumerNewOCA
271 +     """
272 + 
273 +     _header(topology_m1c1, "Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)")
274 + 
275 +     # add this OC on consumer. Supplier will no push the schema
276 +     add_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA')
277 + 
278 +     # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
279 +     time.sleep(2)
280 +     add_OC(topology_m1c1.ms["master1"], 3, 'masterNewOCB')
281 + 
282 +     # now push the scheam
283 +     trigger_schema_push(topology_m1c1)
284 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
285 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
286 + 
287 +     # Check the schemaCSN was NOT updated on the consumer
288 +     # with 47721, supplier learns the missing definition
289 +     log.debug("test_schema_replication_two master_schema_csn=%s", master_schema_csn)
290 +     log.debug("test_schema_replication_two consumer_schema_csn=%s", consumer_schema_csn)
291 +     if support_schema_learning(topology_m1c1):
292 +         assert master_schema_csn == consumer_schema_csn
293 +     else:
294 +         assert master_schema_csn != consumer_schema_csn
295 + 
296 +     # Check the error log of the supplier does not contain an error
297 +     # This message may happen during the learning phase
298 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
299 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
300 + 
301 + 
302 + @pytest.mark.ds47490
303 + def test_schema_replication_three(topology_m1c1, schema_replication_init):
304 +     """Check supplier schema is again a superset (one extra OC), then
305 +     schema is pushed and there is no message in the error log
306 + 
307 +     :id: 45888895-76bc-4cc3-9f90-33a69d027116
308 +     :setup: Master Consumer, check if a warning message is logged in the
309 +     error log of the supplier and add a test entry to trigger attempt of schema push.
310 +     :steps:
311 +         1. Update the schema of master
312 +         2. Push the Schema (no error)
313 +         3. Check the schemaCSN was NOT updated on the consumer
314 +         4. Check the error logs for no errors
315 +         5. Check the startup/final state
316 +     :expectedresults:
317 +         1. Operation should be successful
318 +         2. Operation should be successful
319 +         3. Operation should be successful
320 +         4. Operation should be successful
321 +         5. State at startup
322 +             - supplier +masterNewOCA +masterNewOCB
323 +             - consumer +masterNewOCA               +consumerNewOCA
324 +            Final state
325 +             - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
326 +             - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
327 +     """
328 +     _header(topology_m1c1, "Extra OC Schema is pushed - no error")
329 + 
330 +     # Do an upate to trigger the schema push attempt
331 +     # add this OC on consumer. Supplier will no push the schema
332 +     add_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA')
333 + 
334 +     # now push the scheam
335 +     trigger_schema_push(topology_m1c1)
336 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
337 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
338 + 
339 +     # Check the schemaCSN was NOT updated on the consumer
340 +     log.debug("test_schema_replication_three master_schema_csn=%s", master_schema_csn)
341 +     log.debug("test_schema_replication_three consumer_schema_csn=%s", consumer_schema_csn)
342 +     assert master_schema_csn == consumer_schema_csn
343 + 
344 +     # Check the error log of the supplier does not contain an error
345 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
346 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
347 +     if res is not None:
348 +         assert False
349 + 
350 + 
351 + @pytest.mark.ds47490
352 + def test_schema_replication_four(topology_m1c1, schema_replication_init):
353 +     """Check supplier schema is again a superset (OC with more MUST), then
354 +     schema is pushed and there is no message in the error log
355 + 
356 +     :id: 39304242-2641-4eb8-a9fb-5ff0cf80718f
357 +     :setup: Master Consumer, check if a warning message is logged in the
358 +     error log of the supplier and add a test entry to trigger attempt of schema push.
359 +     :steps:
360 +         1. Add telenumber to 'masterNewOCA' on the master
361 +         2. Push the Schema (no error)
362 +         3. Check the schemaCSN was updated on the consumer
363 +         4. Check the error log of the supplier does not contain an error
364 +         5. Check the startup/final state
365 +     :expectedresults:
366 +         1. Operation should be successful
367 +         2. Operation should be successful
368 +         3. Operation should be successful
369 +         4. Operation should be successful
370 +         5. State at startup
371 +             - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
372 +             - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
373 +            Final state
374 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
375 +                        +must=telexnumber
376 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
377 +                        +must=telexnumber
378 +     """
379 +     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error")
380 + 
381 +     mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
382 +            new_may=MAY_OLD)
383 + 
384 +     trigger_schema_push(topology_m1c1)
385 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
386 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
387 + 
388 +     # Check the schemaCSN was updated on the consumer
389 +     log.debug("test_schema_replication_four master_schema_csn=%s", master_schema_csn)
390 +     log.debug("ctest_schema_replication_four onsumer_schema_csn=%s", consumer_schema_csn)
391 +     assert master_schema_csn == consumer_schema_csn
392 + 
393 +     # Check the error log of the supplier does not contain an error
394 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
395 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
396 +     if res is not None:
397 +         assert False
398 + 
399 + 
400 + @pytest.mark.ds47490
401 + def test_schema_replication_five(topology_m1c1, schema_replication_init):
402 +     """Check consumer schema is  a superset (OC with more MUST), then
403 +     schema is  pushed (fix for 47721) and there is a message in the error log
404 + 
405 +     :id: 498527df-28c8-4e1a-bc9e-799fd2b7b2bb
406 +     :setup: Master Consumer, check if a warning message is logged in the
407 +     error log of the supplier and add a test entry to trigger attempt of schema push.
408 +     :steps:
409 +         1. Add telenumber to 'consumerNewOCA' on the consumer
410 +         2. Add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer
411 +         3. Push the Schema
412 +         4. Check the schemaCSN was NOT updated on the consumer
413 +         5. Check the error log of the supplier contain an error
414 +         6. Check the startup/final state
415 +     :expectedresults:
416 +         1. Operation should be successful
417 +         2. Operation should be successful
418 +         3. Operation should be successful
419 +         4. Operation should be successful
420 +         5. Operation should be successful
421 +         6. State at startup
422 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
423 +                        +must=telexnumber
424 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
425 +                         +must=telexnumber
426 +            Final state
427 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
428 +                        +must=telexnumber
429 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
430 +                        +must=telexnumber                   +must=telexnumber
431 + 
432 +            Note: replication log is enabled to get more details
433 +     """
434 +     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - (fix for 47721)")
435 + 
436 +     # get more detail why it fails
437 +     topology_m1c1.ms["master1"].enableReplLogging()
438 + 
439 +     # add telenumber to 'consumerNewOCA' on the consumer
440 +     mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
441 +            new_may=MAY_OLD)
442 +     # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
443 +     time.sleep(2)
444 +     add_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC')
445 + 
446 +     trigger_schema_push(topology_m1c1)
447 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
448 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
449 + 
450 +     # Check the schemaCSN was NOT updated on the consumer
451 +     # with 47721, supplier learns the missing definition
452 +     log.debug("test_schema_replication_five master_schema_csn=%s", master_schema_csn)
453 +     log.debug("ctest_schema_replication_five onsumer_schema_csn=%s", consumer_schema_csn)
454 +     if support_schema_learning(topology_m1c1):
455 +         assert master_schema_csn == consumer_schema_csn
456 +     else:
457 +         assert master_schema_csn != consumer_schema_csn
458 + 
459 +     # Check the error log of the supplier does not contain an error
460 +     # This message may happen during the learning phase
461 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
462 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
463 + 
464 + 
465 + @pytest.mark.ds47490
466 + def test_schema_replication_six(topology_m1c1, schema_replication_init):
467 +     """Check supplier schema is  again a superset (OC with more MUST), then
468 +     schema is pushed and there is no message in the error log
469 + 
470 +     :id: ed57b0cc-6a10-4f89-94ae-9f18542b1954
471 +     :setup: Master Consumer, check if a warning message is logged in the
472 +     error log of the supplier and add a test entry to trigger attempt of schema push.
473 +     :steps:
474 +         1. Add telenumber to 'consumerNewOCA' on the master
475 +         2. Push the Schema (no error)
476 +         3. Check the schemaCSN was NOT updated on the consumer
477 +         4. Check the error log of the supplier does not contain an error
478 +         5. Check the startup/final state
479 +     :expectedresults:
480 +         1. Operation should be successful
481 +         2. Operation should be successful
482 +         3. Operation should be successful
483 +         4. Operation should be successful
484 +         5. State at startup
485 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
486 +                        +must=telexnumber
487 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
488 +                        +must=telexnumber                   +must=telexnumber
489 +            Final state
490 + 
491 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
492 +                        +must=telexnumber                   +must=telexnumber
493 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
494 +                        +must=telexnumber                   +must=telexnumber
495 +            Note: replication log is enabled to get more details
496 +     """
497 +     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error")
498 + 
499 +     # add telenumber to 'consumerNewOCA' on the consumer
500 +     mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
501 +            new_may=MAY_OLD)
502 + 
503 +     trigger_schema_push(topology_m1c1)
504 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
505 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
506 + 
507 +     # Check the schemaCSN was NOT updated on the consumer
508 +     log.debug("test_schema_replication_six master_schema_csn=%s", master_schema_csn)
509 +     log.debug("ctest_schema_replication_six onsumer_schema_csn=%s", consumer_schema_csn)
510 +     assert master_schema_csn == consumer_schema_csn
511 + 
512 +     # Check the error log of the supplier does not contain an error
513 +     # This message may happen during the learning phase
514 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
515 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
516 +     if res is not None:
517 +         assert False
518 + 
519 + 
520 + @pytest.mark.ds47490
521 + def test_schema_replication_seven(topology_m1c1, schema_replication_init):
522 +     """Check supplier schema is again a superset (OC with more MAY), then
523 +     schema is pushed and there is no message in the error log
524 + 
525 +     :id: 8725055a-b3f8-4d1d-a4d6-bb7dccf644d0
526 +     :setup: Master Consumer, check if a warning message is logged in the
527 +     error log of the supplier and add a test entry to trigger attempt of schema push.
528 +     :steps:
529 +         1. Add telenumber to 'masterNewOCA' on the master
530 +         2. Push the Schema (no error)
531 +         3. Check the schemaCSN was updated on the consumer
532 +         4. Check the error log of the supplier does not contain an error
533 +         5. Check the startup/final state
534 +     :expectedresults:
535 +         1. Operation should be successful
536 +         2. Operation should be successful
537 +         3. Operation should be successful
538 +         4. Operation should be successful
539 +         5. State at startup
540 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
541 +                        +must=telexnumber                   +must=telexnumber
542 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
543 +                        +must=telexnumber                   +must=telexnumber
544 +            Final stat
545 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
546 +                        +must=telexnumber                   +must=telexnumber
547 +                        +may=postOfficeBox
548 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
549 +                        +must=telexnumber                   +must=telexnumber
550 +                        +may=postOfficeBox
551 +     """
552 +     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error")
553 + 
554 +     mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
555 +            new_may=MAY_NEW)
556 + 
557 +     trigger_schema_push(topology_m1c1)
558 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
559 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
560 + 
561 +     # Check the schemaCSN was updated on the consumer
562 +     log.debug("test_schema_replication_seven master_schema_csn=%s", master_schema_csn)
563 +     log.debug("ctest_schema_replication_seven consumer_schema_csn=%s", consumer_schema_csn)
564 +     assert master_schema_csn == consumer_schema_csn
565 + 
566 +     # Check the error log of the supplier does not contain an error
567 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
568 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
569 +     if res is not None:
570 +         assert False
571 + 
572 + 
573 + @pytest.mark.ds47490
574 + def test_schema_replication_eight(topology_m1c1, schema_replication_init):
575 +     """Check consumer schema is a superset (OC with more MAY), then
576 +     schema is  pushed (fix for 47721) and there is  message in the error log
577 + 
578 +     :id: 2310d150-a71a-498d-add8-4056beeb58c6
579 +     :setup: Master Consumer, check if a warning message is logged in the
580 +     error log of the supplier and add a test entry to trigger attempt of schema push.
581 +     :steps:
582 +         1. Add telenumber to 'consumerNewOCA' on the consumer
583 +         2. Modify OC on the supplier so that its nsSchemaCSN is larger than the consumer
584 +         3. Push the Schema (no error)
585 +         4. Check the schemaCSN was updated on the consumer
586 +         5. Check the error log of the supplier does not contain an error
587 +         6. Check the startup/final state
588 +     :expectedresults:
589 +         1. Operation should be successful
590 +         2. Operation should be successful
591 +         3. Operation should be successful
592 +         4. Operation should be successful
593 +         5. Operation should be successful
594 +         6. State at startup
595 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
596 +                        +must=telexnumber                   +must=telexnumber
597 +                        +may=postOfficeBox
598 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
599 +                        +must=telexnumber                   +must=telexnumber
600 +                        +may=postOfficeBox
601 +            Final state
602 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
603 +                        +must=telexnumber                   +must=telexnumber
604 +                        +may=postOfficeBox                                     +may=postOfficeBox
605 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
606 +                        +must=telexnumber                   +must=telexnumber
607 +                        +may=postOfficeBox                  +may=postOfficeBox
608 +     """
609 +     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed (fix for 47721)")
610 + 
611 +     mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
612 +            new_may=MAY_NEW)
613 + 
614 +     # modify OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
615 +     time.sleep(2)
616 +     mod_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC', old_must=MUST_OLD, new_must=MUST_OLD, old_may=MAY_OLD,
617 +            new_may=MAY_NEW)
618 + 
619 +     trigger_schema_push(topology_m1c1)
620 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
621 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
622 + 
623 +     # Check the schemaCSN was not updated on the consumer
624 +     # with 47721, supplier learns the missing definition
625 +     log.debug("test_schema_replication_eight master_schema_csn=%s", master_schema_csn)
626 +     log.debug("ctest_schema_replication_eight onsumer_schema_csn=%s", consumer_schema_csn)
627 +     if support_schema_learning(topology_m1c1):
628 +         assert master_schema_csn == consumer_schema_csn
629 +     else:
630 +         assert master_schema_csn != consumer_schema_csn
631 + 
632 +     # Check the error log of the supplier does not contain an error
633 +     # This message may happen during the learning phase
634 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
635 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
636 + 
637 + 
638 + @pytest.mark.ds47490
639 + def test_schema_replication_nine(topology_m1c1, schema_replication_init):
640 +     """Check consumer schema is a superset (OC with more MAY), then
641 +     schema is  not pushed and there is message in the error log
642 + 
643 +     :id: 851b24c6-b1e0-466f-9714-aa2940fbfeeb
644 +     :setup: Master Consumer, check if a warning message is logged in the
645 +     error log of the supplier and add a test entry to trigger attempt of schema push.
646 +     :steps:
647 +         1. Add postOfficeBox to 'consumerNewOCA' on the master
648 +         3. Push the Schema
649 +         4. Check the schemaCSN was updated on the consumer
650 +         5. Check the error log of the supplier does contain an error
651 +         6. Check the startup/final state
652 +     :expectedresults:
653 +         1. Operation should be successful
654 +         2. Operation should be successful
655 +         3. Operation should be successful
656 +         4. Operation should be successful
657 +         5. Operation should be successful
658 +         6. State at startup
659 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
660 +                        +must=telexnumber                   +must=telexnumber
661 +                        +may=postOfficeBox                                     +may=postOfficeBox
662 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
663 +                        +must=telexnumber                   +must=telexnumber
664 +                        +may=postOfficeBox                  +may=postOfficeBox
665 + 
666 +            Final state
667 + 
668 +             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
669 +                        +must=telexnumber                   +must=telexnumber
670 +                        +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
671 +             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
672 +                        +must=telexnumber                   +must=telexnumber
673 +                        +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
674 +     """
675 +     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error")
676 + 
677 +     mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
678 +            new_may=MAY_NEW)
679 + 
680 +     trigger_schema_push(topology_m1c1)
681 +     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
682 +     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
683 + 
684 +     # Check the schemaCSN was updated on the consumer
685 +     log.debug("test_schema_replication_nine master_schema_csn=%s", master_schema_csn)
686 +     log.debug("ctest_schema_replication_nine onsumer_schema_csn=%s", consumer_schema_csn)
687 +     assert master_schema_csn == consumer_schema_csn
688 + 
689 +     # Check the error log of the supplier does not contain an error
690 +     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
691 +     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
692 +     if res is not None:
693 +         assert False
694 + 
695 +     log.info('Testcase PASSED')
696 + 
697 + 
698 + if __name__ == '__main__':
699 +     # Run isolated
700 +     # -s for DEBUG mode
701 +     CURRENT_FILE = os.path.realpath(__file__)
702 +     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -1,455 +0,0 @@
  2 - # --- BEGIN COPYRIGHT BLOCK ---
  3 - # Copyright (C) 2016 Red Hat, Inc.
  4 - # All rights reserved.
  5 - #
  6 - # License: GPL (version 3 or any later version).
  7 - # See LICENSE for details.
  8 - # --- END COPYRIGHT BLOCK ---
  9 - #
 10 - from subprocess import Popen
 11 - 
 12 - import pytest
 13 - from lib389.paths import Paths
 14 - from lib389.tasks import *
 15 - from lib389.utils import *
 16 - from lib389.topologies import topology_st
 17 - 
 18 - from lib389._constants import DN_DM, DEFAULT_SUFFIX, PASSWORD, SERVERID_STANDALONE
 19 - 
 20 - logging.getLogger(__name__).setLevel(logging.DEBUG)
 21 - log = logging.getLogger(__name__)
 22 - 
 23 - CONFIG_DN = 'cn=config'
 24 - BOU = 'BOU'
 25 - BINDOU = 'ou=%s,%s' % (BOU, DEFAULT_SUFFIX)
 26 - BUID = 'buser123'
 27 - TUID = 'tuser0'
 28 - BINDDN = 'uid=%s,%s' % (BUID, BINDOU)
 29 - BINDPW = BUID
 30 - TESTDN = 'uid=%s,ou=people,%s' % (TUID, DEFAULT_SUFFIX)
 31 - TESTPW = TUID
 32 - BOGUSDN = 'uid=bogus,%s' % DEFAULT_SUFFIX
 33 - BOGUSDN2 = 'uid=bogus,ou=people,%s' % DEFAULT_SUFFIX
 34 - BOGUSSUFFIX = 'uid=bogus,ou=people,dc=bogus'
 35 - GROUPOU = 'ou=groups,%s' % DEFAULT_SUFFIX
 36 - BOGUSOU = 'ou=OU,%s' % DEFAULT_SUFFIX
 37 - 
 38 - 
 39 - def pattern_accesslog(file, log_pattern):
 40 -     for i in range(5):
 41 -         try:
 42 -             pattern_accesslog.last_pos += 1
 43 -         except AttributeError:
 44 -             pattern_accesslog.last_pos = 0
 45 - 
 46 -         found = None
 47 -         file.seek(pattern_accesslog.last_pos)
 48 - 
 49 -         # Use a while true iteration because 'for line in file: hit a
 50 -         # python bug that break file.tell()
 51 -         while True:
 52 -             line = file.readline()
 53 -             found = log_pattern.search(line)
 54 -             if ((line == '') or (found)):
 55 -                 break
 56 - 
 57 -         pattern_accesslog.last_pos = file.tell()
 58 -         if found:
 59 -             return line
 60 -         else:
 61 -             time.sleep(1)
 62 -     return None
 63 - 
 64 - 
 65 - def check_op_result(server, op, dn, superior, exists, rc):
 66 -     targetdn = dn
 67 -     if op == 'search':
 68 -         if exists:
 69 -             opstr = 'Searching existing entry'
 70 -         else:
 71 -             opstr = 'Searching non-existing entry'
 72 -     elif op == 'add':
 73 -         if exists:
 74 -             opstr = 'Adding existing entry'
 75 -         else:
 76 -             opstr = 'Adding non-existing entry'
 77 -     elif op == 'modify':
 78 -         if exists:
 79 -             opstr = 'Modifying existing entry'
 80 -         else:
 81 -             opstr = 'Modifying non-existing entry'
 82 -     elif op == 'modrdn':
 83 -         if superior is not None:
 84 -             targetdn = superior
 85 -             if exists:
 86 -                 opstr = 'Moving to existing superior'
 87 -             else:
 88 -                 opstr = 'Moving to non-existing superior'
 89 -         else:
 90 -             if exists:
 91 -                 opstr = 'Renaming existing entry'
 92 -             else:
 93 -                 opstr = 'Renaming non-existing entry'
 94 -     elif op == 'delete':
 95 -         if exists:
 96 -             opstr = 'Deleting existing entry'
 97 -         else:
 98 -             opstr = 'Deleting non-existing entry'
 99 - 
100 -     if ldap.SUCCESS == rc:
101 -         expstr = 'be ok'
102 -     else:
103 -         expstr = 'fail with %s' % rc.__name__
104 - 
105 -     log.info('%s %s, which should %s.' % (opstr, targetdn, expstr))
106 -     time.sleep(1)
107 -     hit = 0
108 -     try:
109 -         if op == 'search':
110 -             centry = server.search_s(dn, ldap.SCOPE_BASE, 'objectclass=*')
111 -         elif op == 'add':
112 -             server.add_s(Entry((dn, {'objectclass': 'top extensibleObject'.split(),
113 -                                      'cn': 'test entry'})))
114 -         elif op == 'modify':
115 -             server.modify_s(dn, [(ldap.MOD_REPLACE, 'description', 'test')])
116 -         elif op == 'modrdn':
117 -             if superior is not None:
118 -                 server.rename_s(dn, 'uid=new', newsuperior=superior, delold=1)
119 -             else:
120 -                 server.rename_s(dn, 'uid=new', delold=1)
121 -         elif op == 'delete':
122 -             server.delete_s(dn)
123 -         else:
124 -             log.fatal('Unknown operation %s' % op)
125 -             assert False
126 -     except ldap.LDAPError as e:
127 -         hit = 1
128 -         log.info("Exception (expected): %s" % type(e).__name__)
129 -         log.info('Desc ' + e.message['desc'])
130 -         assert isinstance(e, rc)
131 -         if 'matched' in e.message:
132 -             log.info('Matched is returned: ' + e.message['matched'])
133 -             if rc != ldap.NO_SUCH_OBJECT:
134 -                 assert False
135 - 
136 -     if ldap.SUCCESS == rc:
137 -         if op == 'search':
138 -             log.info('Search should return none')
139 -             assert len(centry) == 0
140 -     else:
141 -         if 0 == hit:
142 -             log.info('Expected to fail with %s, but passed' % rc.__name__)
143 -             assert False
144 - 
145 -     log.info('PASSED\n')
146 - 
147 - 
148 - def test_ticket1347760(topology_st):
149 -     """
150 -     Prevent revealing the entry info to whom has no access rights.
151 -     """
152 -     log.info('Testing Bug 1347760 - Information disclosure via repeated use of LDAP ADD operation, etc.')
153 - 
154 -     log.info('Disabling accesslog logbuffering')
155 -     topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-accesslog-logbuffering', 'off')])
156 - 
157 -     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
158 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
159 - 
160 -     log.info('Adding ou=%s a bind user belongs to.' % BOU)
161 -     topology_st.standalone.add_s(Entry((BINDOU, {
162 -         'objectclass': 'top organizationalunit'.split(),
163 -         'ou': BOU})))
164 - 
165 -     log.info('Adding a bind user.')
166 -     topology_st.standalone.add_s(Entry((BINDDN,
167 -                                         {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
168 -                                          'cn': 'bind user',
169 -                                          'sn': 'user',
170 -                                          'userPassword': BINDPW})))
171 - 
172 -     log.info('Adding a test user.')
173 -     topology_st.standalone.add_s(Entry((TESTDN,
174 -                                         {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
175 -                                          'cn': 'test user',
176 -                                          'sn': 'user',
177 -                                          'userPassword': TESTPW})))
178 - 
179 -     log.info('Deleting aci in %s.' % DEFAULT_SUFFIX)
180 -     topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)])
181 - 
182 -     log.info('While binding as DM, acquire an access log path and instance dir')
183 -     ds_paths = Paths(serverid=topology_st.standalone.serverid,
184 -                      instance=topology_st.standalone)
185 -     file_path = ds_paths.access_log
186 -     inst_dir = ds_paths.inst_dir
187 - 
188 -     log.info('Bind case 1. the bind user has no rights to read the entry itself, bind should be successful.')
189 -     log.info('Bind as {%s,%s} who has no access rights.' % (BINDDN, BINDPW))
190 -     try:
191 -         topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
192 -     except ldap.LDAPError as e:
193 -         log.info('Desc ' + e.message['desc'])
194 -         assert False
195 - 
196 -     file_obj = open(file_path, "r")
197 -     log.info('Access log path: %s' % file_path)
198 - 
199 -     log.info(
200 -         'Bind case 2-1. the bind user does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
201 -     log.info('Bind as {%s,%s} who does not exist.' % (BOGUSDN, 'bogus'))
202 -     try:
203 -         topology_st.standalone.simple_bind_s(BOGUSDN, 'bogus')
204 -     except ldap.LDAPError as e:
205 -         log.info("Exception (expected): %s" % type(e).__name__)
206 -         log.info('Desc ' + e.message['desc'])
207 -         assert isinstance(e, ldap.INVALID_CREDENTIALS)
208 -         regex = re.compile('No such entry')
209 -         cause = pattern_accesslog(file_obj, regex)
210 -         if cause is None:
211 -             log.fatal('Cause not found - %s' % cause)
212 -             assert False
213 -         else:
214 -             log.info('Cause found - %s' % cause)
215 -     time.sleep(1)
216 - 
217 -     log.info(
218 -         'Bind case 2-2. the bind user\'s suffix does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
219 -     log.info('Bind as {%s,%s} who does not exist.' % (BOGUSSUFFIX, 'bogus'))
220 -     with pytest.raises(ldap.INVALID_CREDENTIALS):
221 -         topology_st.standalone.simple_bind_s(BOGUSSUFFIX, 'bogus')
222 -     regex = re.compile('No suffix for bind')
223 -     cause = pattern_accesslog(file_obj, regex)
224 -     if cause is None:
225 -         log.fatal('Cause not found - %s' % cause)
226 -         assert False
227 -     else:
228 -         log.info('Cause found - %s' % cause)
229 -     time.sleep(1)
230 - 
231 -     log.info(
232 -         'Bind case 2-3. the bind user\'s password is wrong, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__)
233 -     log.info('Bind as {%s,%s} who does not exist.' % (BINDDN, 'bogus'))
234 -     try:
235 -         topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
236 -     except ldap.LDAPError as e:
237 -         log.info("Exception (expected): %s" % type(e).__name__)
238 -         log.info('Desc ' + e.message['desc'])
239 -         assert isinstance(e, ldap.INVALID_CREDENTIALS)
240 -         regex = re.compile('Invalid credentials')
241 -         cause = pattern_accesslog(file_obj, regex)
242 -         if cause is None:
243 -             log.fatal('Cause not found - %s' % cause)
244 -             assert False
245 -         else:
246 -             log.info('Cause found - %s' % cause)
247 -     time.sleep(1)
248 - 
249 -     log.info('Adding aci for %s to %s.' % (BINDDN, BINDOU))
250 -     acival = '(targetattr="*")(version 3.0; acl "%s"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
251 -     log.info('aci: %s' % acival)
252 -     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
253 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
254 -     topology_st.standalone.modify_s(BINDOU, [(ldap.MOD_ADD, 'aci', acival)])
255 -     time.sleep(1)
256 - 
257 -     log.info('Bind case 3. the bind user has the right to read the entry itself, bind should be successful.')
258 -     log.info('Bind as {%s,%s} which should be ok.\n' % (BINDDN, BINDPW))
259 -     topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
260 - 
261 -     log.info('The following operations are against the subtree the bind user %s has no rights.' % BINDDN)
262 -     # Search
263 -     exists = True
264 -     rc = ldap.SUCCESS
265 -     log.info(
266 -         'Search case 1. the bind user has no rights to read the search entry, it should return no search results with %s' % rc)
267 -     check_op_result(topology_st.standalone, 'search', TESTDN, None, exists, rc)
268 - 
269 -     exists = False
270 -     rc = ldap.SUCCESS
271 -     log.info(
272 -         'Search case 2-1. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
273 -     check_op_result(topology_st.standalone, 'search', BOGUSDN, None, exists, rc)
274 - 
275 -     exists = False
276 -     rc = ldap.SUCCESS
277 -     log.info(
278 -         'Search case 2-2. the search entry does not exist, the search should return no search results with %s' % rc.__name__)
279 -     check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
280 - 
281 -     # Add
282 -     exists = True
283 -     rc = ldap.INSUFFICIENT_ACCESS
284 -     log.info(
285 -         'Add case 1. the bind user has no rights AND the adding entry exists, it should fail with %s' % rc.__name__)
286 -     check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
287 - 
288 -     exists = False
289 -     rc = ldap.INSUFFICIENT_ACCESS
290 -     log.info(
291 -         'Add case 2-1. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
292 -     check_op_result(topology_st.standalone, 'add', BOGUSDN, None, exists, rc)
293 - 
294 -     exists = False
295 -     rc = ldap.INSUFFICIENT_ACCESS
296 -     log.info(
297 -         'Add case 2-2. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__)
298 -     check_op_result(topology_st.standalone, 'add', BOGUSDN2, None, exists, rc)
299 - 
300 -     # Modify
301 -     exists = True
302 -     rc = ldap.INSUFFICIENT_ACCESS
303 -     log.info(
304 -         'Modify case 1. the bind user has no rights AND the modifying entry exists, it should fail with %s' % rc.__name__)
305 -     check_op_result(topology_st.standalone, 'modify', TESTDN, None, exists, rc)
306 - 
307 -     exists = False
308 -     rc = ldap.INSUFFICIENT_ACCESS
309 -     log.info(
310 -         'Modify case 2-1. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
311 -     check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
312 - 
313 -     exists = False
314 -     rc = ldap.INSUFFICIENT_ACCESS
315 -     log.info(
316 -         'Modify case 2-2. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__)
317 -     check_op_result(topology_st.standalone, 'modify', BOGUSDN2, None, exists, rc)
318 - 
319 -     # Modrdn
320 -     exists = True
321 -     rc = ldap.INSUFFICIENT_ACCESS
322 -     log.info(
323 -         'Modrdn case 1. the bind user has no rights AND the renaming entry exists, it should fail with %s' % rc.__name__)
324 -     check_op_result(topology_st.standalone, 'modrdn', TESTDN, None, exists, rc)
325 - 
326 -     exists = False
327 -     rc = ldap.INSUFFICIENT_ACCESS
328 -     log.info(
329 -         'Modrdn case 2-1. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
330 -     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
331 - 
332 -     exists = False
333 -     rc = ldap.INSUFFICIENT_ACCESS
334 -     log.info(
335 -         'Modrdn case 2-2. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__)
336 -     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN2, None, exists, rc)
337 - 
338 -     exists = True
339 -     rc = ldap.INSUFFICIENT_ACCESS
340 -     log.info(
341 -         'Modrdn case 3. the bind user has no rights AND the node moving an entry to exists, it should fail with %s' % rc.__name__)
342 -     check_op_result(topology_st.standalone, 'modrdn', TESTDN, GROUPOU, exists, rc)
343 - 
344 -     exists = False
345 -     rc = ldap.INSUFFICIENT_ACCESS
346 -     log.info(
347 -         'Modrdn case 4-1. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
348 -     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
349 - 
350 -     exists = False
351 -     rc = ldap.INSUFFICIENT_ACCESS
352 -     log.info(
353 -         'Modrdn case 4-2. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__)
354 -     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
355 - 
356 -     # Delete
357 -     exists = True
358 -     rc = ldap.INSUFFICIENT_ACCESS
359 -     log.info(
360 -         'Delete case 1. the bind user has no rights AND the deleting entry exists, it should fail with %s' % rc.__name__)
361 -     check_op_result(topology_st.standalone, 'delete', TESTDN, None, exists, rc)
362 - 
363 -     exists = False
364 -     rc = ldap.INSUFFICIENT_ACCESS
365 -     log.info(
366 -         'Delete case 2-1. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
367 -     check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
368 - 
369 -     exists = False
370 -     rc = ldap.INSUFFICIENT_ACCESS
371 -     log.info(
372 -         'Delete case 2-2. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__)
373 -     check_op_result(topology_st.standalone, 'delete', BOGUSDN2, None, exists, rc)
374 - 
375 -     log.info('EXTRA: Check no regressions')
376 -     log.info('Adding aci for %s to %s.' % (BINDDN, DEFAULT_SUFFIX))
377 -     acival = '(targetattr="*")(version 3.0; acl "%s-all"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN)
378 -     log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD))
379 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
380 -     topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', acival)])
381 -     time.sleep(1)
382 - 
383 -     log.info('Bind as {%s,%s}.' % (BINDDN, BINDPW))
384 -     try:
385 -         topology_st.standalone.simple_bind_s(BINDDN, BINDPW)
386 -     except ldap.LDAPError as e:
387 -         log.info('Desc ' + e.message['desc'])
388 -         assert False
389 -     time.sleep(1)
390 - 
391 -     exists = False
392 -     rc = ldap.NO_SUCH_OBJECT
393 -     log.info('Search case. the search entry does not exist, the search should fail with %s' % rc.__name__)
394 -     check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc)
395 -     file_obj.close()
396 - 
397 -     exists = True
398 -     rc = ldap.ALREADY_EXISTS
399 -     log.info('Add case. the adding entry already exists, it should fail with %s' % rc.__name__)
400 -     check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc)
401 - 
402 -     exists = False
403 -     rc = ldap.NO_SUCH_OBJECT
404 -     log.info('Modify case. the modifying entry does not exist, it should fail with %s' % rc.__name__)
405 -     check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc)
406 - 
407 -     exists = False
408 -     rc = ldap.NO_SUCH_OBJECT
409 -     log.info('Modrdn case 1. the renaming entry does not exist, it should fail with %s' % rc.__name__)
410 -     check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc)
411 - 
412 -     exists = False
413 -     rc = ldap.NO_SUCH_OBJECT
414 -     log.info('Modrdn case 2. the node moving an entry to does not, it should fail with %s' % rc.__name__)
415 -     check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc)
416 - 
417 -     exists = False
418 -     rc = ldap.NO_SUCH_OBJECT
419 -     log.info('Delete case. the deleting entry does not exist, it should fail with %s' % rc.__name__)
420 -     check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc)
421 - 
422 -     log.info('Inactivate %s' % BINDDN)
423 -     if ds_paths.version < '1.3':
424 -         nsinactivate = '%s/ns-inactivate.pl' % inst_dir
425 -         nsinactivate_cmd = [nsinactivate, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN]
426 -     else:
427 -         nsinactivate = '%s/ns-inactivate.pl' % ds_paths.sbin_dir
428 -         nsinactivate_cmd = [nsinactivate, '-Z', SERVERID_STANDALONE, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN]
429 -     log.info(nsinactivate_cmd)
430 -     p = Popen(nsinactivate_cmd)
431 -     assert (p.wait() == 0)
432 - 
433 -     log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, BUID, ldap.UNWILLING_TO_PERFORM.__name__))
434 -     try:
435 -         topology_st.standalone.simple_bind_s(BINDDN, BUID)
436 -     except ldap.LDAPError as e:
437 -         log.info("Exception (expected): %s" % type(e).__name__)
438 -         log.info('Desc ' + e.message['desc'])
439 -         assert isinstance(e, ldap.UNWILLING_TO_PERFORM)
440 - 
441 -     log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, 'bogus', ldap.UNWILLING_TO_PERFORM.__name__))
442 -     try:
443 -         topology_st.standalone.simple_bind_s(BINDDN, 'bogus')
444 -     except ldap.LDAPError as e:
445 -         log.info("Exception (expected): %s" % type(e).__name__)
446 -         log.info('Desc ' + e.message['desc'])
447 -         assert isinstance(e, ldap.UNWILLING_TO_PERFORM)
448 - 
449 -     log.info('SUCCESS')
450 - 
451 - 
452 - if __name__ == '__main__':
453 -     # Run isolated
454 -     # -s for DEBUG mode
455 -     CURRENT_FILE = os.path.realpath(__file__)
456 -     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -1,208 +0,0 @@
  2 - # --- BEGIN COPYRIGHT BLOCK ---
  3 - # Copyright (C) 2016 Red Hat, Inc.
  4 - # All rights reserved.
  5 - #
  6 - # License: GPL (version 3 or any later version).
  7 - # See LICENSE for details.
  8 - # --- END COPYRIGHT BLOCK ---
  9 - #
 10 - import pytest
 11 - from lib389.tasks import *
 12 - from lib389.utils import *
 13 - from lib389.topologies import topology_st
 14 - 
 15 - from lib389._constants import DEFAULT_SUFFIX, DN_PLUGIN, SUFFIX, PLUGIN_7_BIT_CHECK
 16 - 
 17 - # Skip on older versions
 18 - pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented")
 19 - 
 20 - logging.getLogger(__name__).setLevel(logging.DEBUG)
 21 - log = logging.getLogger(__name__)
 22 - 
 23 - DN_7BITPLUGIN = "cn=7-bit check,%s" % DN_PLUGIN
 24 - ATTRS = ["uid", "mail", "userpassword", ",", SUFFIX, None]
 25 - 
 26 - 
 27 - def test_ticket47431_0(topology_st):
 28 -     '''
 29 -     Enable 7 bit plugin
 30 -     '''
 31 -     log.info("Ticket 47431 - 0: Enable 7bit plugin...")
 32 -     topology_st.standalone.plugins.enable(name=PLUGIN_7_BIT_CHECK)
 33 - 
 34 - def test_ticket47431_1(topology_st):
 35 -     '''
 36 -     nsslapd-pluginarg0: uid
 37 -     nsslapd-pluginarg1: mail
 38 -     nsslapd-pluginarg2: userpassword <== repeat 27 times
 39 -     nsslapd-pluginarg3: ,
 40 -     nsslapd-pluginarg4: dc=example,dc=com
 41 - 
 42 -     The duplicated values are removed by str2entry_dupcheck as follows:
 43 -     [..] - str2entry_dupcheck: 27 duplicate values for attribute type nsslapd-pluginarg2
 44 -            detected in entry cn=7-bit check,cn=plugins,cn=config. Extra values ignored.
 45 -     '''
 46 - 
 47 -     log.info("Ticket 47431 - 1: Check 26 duplicate values are treated as one...")
 48 -     expected = "str2entry_dupcheck.* duplicate values for attribute type nsslapd-pluginarg2 detected in entry cn=7-bit check,cn=plugins,cn=config."
 49 - 
 50 -     log.debug('modify_s %s' % DN_7BITPLUGIN)
 51 -     try:
 52 -         topology_st.standalone.modify_s(DN_7BITPLUGIN,
 53 -                                         [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', "uid"),
 54 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "mail"),
 55 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', "userpassword"),
 56 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', ","),
 57 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg4', SUFFIX)])
 58 -     except ValueError:
 59 -         log.error('modify failed: Some problem occured with a value that was provided')
 60 -         assert False
 61 - 
 62 -     arg2 = "nsslapd-pluginarg2: userpassword"
 63 -     topology_st.standalone.stop()
 64 -     dse_ldif = topology_st.standalone.confdir + '/dse.ldif'
 65 -     os.system('mv %s %s.47431' % (dse_ldif, dse_ldif))
 66 -     os.system(
 67 -         'sed -e "s/\\(%s\\)/\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1/" %s.47431 > %s' % (
 68 -         arg2, dse_ldif, dse_ldif))
 69 -     topology_st.standalone.start()
 70 - 
 71 -     cmdline = 'egrep -i "%s" %s' % (expected, topology_st.standalone.errlog)
 72 -     p = os.popen(cmdline, "r")
 73 -     line = p.readline()
 74 -     if line == "":
 75 -         log.error('Expected error "%s" not logged in %s' % (expected, topology_st.standalone.errlog))
 76 -         assert False
 77 -     else:
 78 -         log.debug('line: %s' % line)
 79 -         log.info('Expected error "%s" logged in %s' % (expected, topology_st.standalone.errlog))
 80 - 
 81 -     log.info("Ticket 47431 - 1: done")
 82 - 
 83 - 
 84 - def test_ticket47431_2(topology_st):
 85 -     '''
 86 -     nsslapd-pluginarg0: uid
 87 -     nsslapd-pluginarg0: mail
 88 -     nsslapd-pluginarg1: userpassword
 89 -     nsslapd-pluginarg2: ,
 90 -     nsslapd-pluginarg3: dc=example,dc=com
 91 -     ==>
 92 -     nsslapd-pluginarg0: uid
 93 -     nsslapd-pluginarg1: mail
 94 -     nsslapd-pluginarg2: userpassword
 95 -     nsslapd-pluginarg3: ,
 96 -     nsslapd-pluginarg4: dc=example,dc=com
 97 -     Should be logged in error log:
 98 -     [..] NS7bitAttr_Init - 0: uid
 99 -     [..] NS7bitAttr_Init - 1: userpassword
100 -     [..] NS7bitAttr_Init - 2: mail
101 -     [..] NS7bitAttr_Init - 3: ,
102 -     [..] NS7bitAttr_Init - 4: dc=example,dc=com
103 -     '''
104 - 
105 -     log.info("Ticket 47431 - 2: Check two values belonging to one arg is fixed...")
106 - 
107 -     try:
108 -         topology_st.standalone.modify_s(DN_7BITPLUGIN,
109 -                                         [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', "uid"),
110 -                                          (ldap.MOD_ADD, 'nsslapd-pluginarg0', "mail"),
111 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "userpassword"),
112 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', ","),
113 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', SUFFIX),
114 -                                          (ldap.MOD_DELETE, 'nsslapd-pluginarg4', None)])
115 -     except ValueError:
116 -         log.error('modify failed: Some problem occured with a value that was provided')
117 -         assert False
118 - 
119 -     # PLUGIN LOG LEVEL
120 -     topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')])
121 - 
122 -     topology_st.standalone.restart()
123 - 
124 -     cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog)
125 -     p = os.popen(cmdline, "r")
126 -     i = 0
127 -     while ATTRS[i]:
128 -         line = p.readline()
129 -         log.debug('line - %s' % line)
130 -         log.debug('ATTRS[%d] %s' % (i, ATTRS[i]))
131 -         if line == "":
132 -             break
133 -         elif line.find(ATTRS[i]) >= 0:
134 -             log.debug('%s was logged' % ATTRS[i])
135 -         else:
136 -             log.error('%s was not logged.' % ATTRS[i])
137 -             assert False
138 -         i = i + 1
139 - 
140 -     log.info("Ticket 47431 - 2: done")
141 - 
142 - 
143 - def test_ticket47431_3(topology_st):
144 -     '''
145 -     nsslapd-pluginarg1: uid
146 -     nsslapd-pluginarg3: mail
147 -     nsslapd-pluginarg5: userpassword
148 -     nsslapd-pluginarg7: ,
149 -     nsslapd-pluginarg9: dc=example,dc=com
150 -     ==>
151 -     nsslapd-pluginarg0: uid
152 -     nsslapd-pluginarg1: mail
153 -     nsslapd-pluginarg2: userpassword
154 -     nsslapd-pluginarg3: ,
155 -     nsslapd-pluginarg4: dc=example,dc=com
156 -     Should be logged in error log:
157 -     [..] NS7bitAttr_Init - 0: uid
158 -     [..] NS7bitAttr_Init - 1: userpassword
159 -     [..] NS7bitAttr_Init - 2: mail
160 -     [..] NS7bitAttr_Init - 3: ,
161 -     [..] NS7bitAttr_Init - 4: dc=example,dc=com
162 -     '''
163 - 
164 -     log.info("Ticket 47431 - 3: Check missing args are fixed...")
165 - 
166 -     try:
167 -         topology_st.standalone.modify_s(DN_7BITPLUGIN,
168 -                                         [(ldap.MOD_DELETE, 'nsslapd-pluginarg0', None),
169 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "uid"),
170 -                                          (ldap.MOD_DELETE, 'nsslapd-pluginarg2', None),
171 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', "mail"),
172 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg5', "userpassword"),
173 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg7', ","),
174 -                                          (ldap.MOD_REPLACE, 'nsslapd-pluginarg9', SUFFIX)])
175 -     except ValueError:
176 -         log.error('modify failed: Some problem occured with a value that was provided')
177 -         assert False
178 - 
179 -     # PLUGIN LOG LEVEL
180 -     topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')])
181 - 
182 -     topology_st.standalone.stop()
183 -     os.system('mv %s %s.47431' % (topology_st.standalone.errlog, topology_st.standalone.errlog))
184 -     os.system('touch %s' % (topology_st.standalone.errlog))
185 -     topology_st.standalone.start()
186 - 
187 -     cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog)
188 -     p = os.popen(cmdline, "r")
189 -     i = 0
190 -     while ATTRS[i]:
191 -         line = p.readline()
192 -         if line == "":
193 -             break
194 -         elif line.find(ATTRS[i]) >= 0:
195 -             log.debug('%s was logged' % ATTRS[i])
196 -         else:
197 -             log.error('%s was not logged.' % ATTRS[i])
198 -             assert False
199 -         i = i + 1
200 - 
201 -     log.info("Ticket 47431 - 3: done")
202 -     log.info('Test complete')
203 - 
204 - 
205 - if __name__ == '__main__':
206 -     # Run isolated
207 -     # -s for DEBUG mode
208 -     CURRENT_FILE = os.path.realpath(__file__)
209 -     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -1,582 +0,0 @@
  2 - # --- BEGIN COPYRIGHT BLOCK ---
  3 - # Copyright (C) 2016 Red Hat, Inc.
  4 - # All rights reserved.
  5 - #
  6 - # License: GPL (version 3 or any later version).
  7 - # See LICENSE for details.
  8 - # --- END COPYRIGHT BLOCK ---
  9 - #
 10 - '''
 11 - Created on Nov 7, 2013
 12 - 
 13 - @author: tbordaz
 14 - '''
 15 - import logging
 16 - import re
 17 - import time
 18 - 
 19 - import ldap
 20 - import pytest
 21 - from lib389 import Entry
 22 - from lib389._constants import *
 23 - from lib389.topologies import topology_m1c1
 24 - 
 25 - from lib389.utils import *
 26 - 
 27 - # Skip on older versions
 28 - pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented")
 29 - logging.getLogger(__name__).setLevel(logging.DEBUG)
 30 - log = logging.getLogger(__name__)
 31 - 
 32 - TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
 33 - ENTRY_DN = "cn=test_entry, %s" % SUFFIX
 34 - MUST_OLD = "(postalAddress $ preferredLocale)"
 35 - MUST_NEW = "(postalAddress $ preferredLocale $ telexNumber)"
 36 - MAY_OLD = "(postalCode $ street)"
 37 - MAY_NEW = "(postalCode $ street $ postOfficeBox)"
 38 - 
 39 - 
 40 - def _header(topology_m1c1, label):
 41 -     topology_m1c1.ms["master1"].log.info("\n\n###############################################")
 42 -     topology_m1c1.ms["master1"].log.info("#######")
 43 -     topology_m1c1.ms["master1"].log.info("####### %s" % label)
 44 -     topology_m1c1.ms["master1"].log.info("#######")
 45 -     topology_m1c1.ms["master1"].log.info("###################################################")
 46 - 
 47 - 
 48 - def pattern_errorlog(file, log_pattern):
 49 -     try:
 50 -         pattern_errorlog.last_pos += 1
 51 -     except AttributeError:
 52 -         pattern_errorlog.last_pos = 0
 53 - 
 54 -     found = None
 55 -     log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos)
 56 -     file.seek(pattern_errorlog.last_pos)
 57 - 
 58 -     # Use a while true iteration because 'for line in file: hit a
 59 -     # python bug that break file.tell()
 60 -     while True:
 61 -         line = file.readline()
 62 -         log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line))
 63 -         found = log_pattern.search(line)
 64 -         if ((line == '') or (found)):
 65 -             break
 66 - 
 67 -     log.debug("_pattern_errorlog: end at offset %d" % file.tell())
 68 -     pattern_errorlog.last_pos = file.tell()
 69 -     return found
 70 - 
 71 - 
 72 - def _oc_definition(oid_ext, name, must=None, may=None):
 73 -     oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
 74 -     desc = 'To test ticket 47490'
 75 -     sup = 'person'
 76 -     if not must:
 77 -         must = MUST_OLD
 78 -     if not may:
 79 -         may = MAY_OLD
 80 - 
 81 -     new_oc = "( %s  NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
 82 -     return new_oc
 83 - 
 84 - 
 85 - def add_OC(instance, oid_ext, name):
 86 -     new_oc = _oc_definition(oid_ext, name)
 87 -     instance.schema.add_schema('objectClasses', new_oc)
 88 - 
 89 - 
 90 - def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None):
 91 -     old_oc = _oc_definition(oid_ext, name, old_must, old_may)
 92 -     new_oc = _oc_definition(oid_ext, name, new_must, new_may)
 93 -     instance.schema.del_schema('objectClasses', old_oc)
 94 -     instance.schema.add_schema('objectClasses', new_oc)
 95 - 
 96 - 
 97 - def support_schema_learning(topology_m1c1):
 98 -     """
 99 -     with https://fedorahosted.org/389/ticket/47721, the supplier and consumer can learn
100 -     schema definitions when a replication occurs.
101 -     Before that ticket: replication of the schema fails requiring administrative operation
102 -     In the test the schemaCSN (master consumer) differs
103 - 
104 -     After that ticket: replication of the schema succeeds (after an initial phase of learning)
105 -     In the test the schema CSN (master consumer) are in sync
106 - 
107 -     This function returns True if 47721 is fixed in the current release
108 -     False else
109 -     """
110 -     ent = topology_m1c1.cs["consumer1"].getEntry(DN_CONFIG, ldap.SCOPE_BASE, "(cn=config)", ['nsslapd-versionstring'])
111 -     if ent.hasAttr('nsslapd-versionstring'):
112 -         val = ent.getValue('nsslapd-versionstring')
113 -         version = val.split('/')[1].split('.')  # something like ['1', '3', '1', '23', 'final_fix']
114 -         major = int(version[0])
115 -         minor = int(version[1])
116 -         if major > 1:
117 -             return True
118 -         if minor > 3:
119 -             # version is 1.4 or after
120 -             return True
121 -         if minor == 3:
122 -             if version[2].isdigit():
123 -                 if int(version[2]) >= 3:
124 -                     return True
125 -         return False
126 - 
127 - 
128 - def trigger_update(topology_m1c1):
129 -     """
130 -         It triggers an update on the supplier. This will start a replication
131 -         session and a schema push
132 -     """
133 -     try:
134 -         trigger_update.value += 1
135 -     except AttributeError:
136 -         trigger_update.value = 1
137 -     replace = [(ldap.MOD_REPLACE, 'telephonenumber', str(trigger_update.value))]
138 -     topology_m1c1.ms["master1"].modify_s(ENTRY_DN, replace)
139 - 
140 -     # wait 10 seconds that the update is replicated
141 -     loop = 0
142 -     while loop <= 10:
143 -         try:
144 -             ent = topology_m1c1.cs["consumer1"].getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)",
145 -                                                          ['telephonenumber'])
146 -             val = ent.telephonenumber or "0"
147 -             if int(val) == trigger_update.value:
148 -                 return
149 -             # the expected value is not yet replicated. try again
150 -             time.sleep(1)
151 -             loop += 1
152 -             log.debug("trigger_update: receive %s (expected %d)" % (val, trigger_update.value))
153 -         except ldap.NO_SUCH_OBJECT:
154 -             time.sleep(1)
155 -             loop += 1
156 - 
157 - 
158 - def trigger_schema_push(topology_m1c1):
159 -     '''
160 -     Trigger update to create a replication session.
161 -     In case of 47721 is fixed and the replica needs to learn the missing definition, then
162 -     the first replication session learn the definition and the second replication session
163 -     push the schema (and the schemaCSN.
164 -     This is why there is two updates and replica agreement is stopped/start (to create a second session)
165 -     '''
166 -     agreements = topology_m1c1.ms["master1"].agreement.list(suffix=SUFFIX,
167 -                                                             consumer_host=topology_m1c1.cs["consumer1"].host,
168 -                                                             consumer_port=topology_m1c1.cs["consumer1"].port)
169 -     assert (len(agreements) == 1)
170 -     ra = agreements[0]
171 -     trigger_update(topology_m1c1)
172 -     topology_m1c1.ms["master1"].agreement.pause(ra.dn)
173 -     topology_m1c1.ms["master1"].agreement.resume(ra.dn)
174 -     trigger_update(topology_m1c1)
175 - 
176 - 
177 - def test_ticket47490_init(topology_m1c1):
178 -     """
179 -         Initialize the test environment
180 -     """
181 -     log.debug("test_ticket47490_init topology_m1c1 %r (master %r, consumer %r" % (
182 -     topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
183 -     # the test case will check if a warning message is logged in the
184 -     # error log of the supplier
185 -     topology_m1c1.ms["master1"].errorlog_file = open(topology_m1c1.ms["master1"].errlog, "r")
186 - 
187 -     # This entry will be used to trigger attempt of schema push
188 -     topology_m1c1.ms["master1"].add_s(Entry((ENTRY_DN, {
189 -         'objectclass': "top person".split(),
190 -         'sn': 'test_entry',
191 -         'cn': 'test_entry'})))
192 - 
193 - 
194 - def test_ticket47490_one(topology_m1c1):
195 -     """
196 -         Summary: Extra OC Schema is pushed - no error
197 - 
198 -         If supplier schema is a superset (one extra OC) of consumer schema, then
199 -         schema is pushed and there is no message in the error log
200 -         State at startup:
201 -             - supplier default schema
202 -             - consumer default schema
203 -         Final state
204 -             - supplier +masterNewOCA
205 -             - consumer +masterNewOCA
206 - 
207 -     """
208 -     _header(topology_m1c1, "Extra OC Schema is pushed - no error")
209 - 
210 -     log.debug("test_ticket47490_one topology_m1c1 %r (master %r, consumer %r" % (
211 -     topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"]))
212 -     # update the schema of the supplier so that it is a superset of
213 -     # consumer. Schema should be pushed
214 -     add_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA')
215 - 
216 -     trigger_schema_push(topology_m1c1)
217 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
218 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
219 - 
220 -     # Check the schemaCSN was updated on the consumer
221 -     log.debug("test_ticket47490_one master_schema_csn=%s", master_schema_csn)
222 -     log.debug("ctest_ticket47490_one onsumer_schema_csn=%s", consumer_schema_csn)
223 -     assert master_schema_csn == consumer_schema_csn
224 - 
225 -     # Check the error log of the supplier does not contain an error
226 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
227 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
228 -     if res is not None:
229 -         assert False
230 - 
231 - 
232 - def test_ticket47490_two(topology_m1c1):
233 -     """
234 -         Summary: Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)
235 - 
236 -         If consumer schema is a superset (one extra OC) of supplier schema, then
237 -         schema is pushed and there is a message in the error log
238 -         State at startup
239 -             - supplier +masterNewOCA
240 -             - consumer +masterNewOCA
241 -         Final state
242 -             - supplier +masterNewOCA +masterNewOCB
243 -             - consumer +masterNewOCA               +consumerNewOCA
244 - 
245 -     """
246 - 
247 -     _header(topology_m1c1, "Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)")
248 - 
249 -     # add this OC on consumer. Supplier will no push the schema
250 -     add_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA')
251 - 
252 -     # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
253 -     time.sleep(2)
254 -     add_OC(topology_m1c1.ms["master1"], 3, 'masterNewOCB')
255 - 
256 -     # now push the scheam
257 -     trigger_schema_push(topology_m1c1)
258 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
259 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
260 - 
261 -     # Check the schemaCSN was NOT updated on the consumer
262 -     # with 47721, supplier learns the missing definition
263 -     log.debug("test_ticket47490_two master_schema_csn=%s", master_schema_csn)
264 -     log.debug("test_ticket47490_two consumer_schema_csn=%s", consumer_schema_csn)
265 -     if support_schema_learning(topology_m1c1):
266 -         assert master_schema_csn == consumer_schema_csn
267 -     else:
268 -         assert master_schema_csn != consumer_schema_csn
269 - 
270 -     # Check the error log of the supplier does not contain an error
271 -     # This message may happen during the learning phase
272 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
273 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
274 - 
275 - 
276 - def test_ticket47490_three(topology_m1c1):
277 -     """
278 -         Summary: Extra OC Schema is pushed - no error
279 - 
280 -         If supplier schema is again a superset (one extra OC), then
281 -         schema is  pushed and there is no message in the error log
282 -         State at startup
283 -             - supplier +masterNewOCA +masterNewOCB
284 -             - consumer +masterNewOCA               +consumerNewOCA
285 -         Final state
286 -             - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
287 -             - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
288 - 
289 -     """
290 -     _header(topology_m1c1, "Extra OC Schema is pushed - no error")
291 - 
292 -     # Do an upate to trigger the schema push attempt
293 -     # add this OC on consumer. Supplier will no push the schema
294 -     add_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA')
295 - 
296 -     # now push the scheam
297 -     trigger_schema_push(topology_m1c1)
298 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
299 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
300 - 
301 -     # Check the schemaCSN was NOT updated on the consumer
302 -     log.debug("test_ticket47490_three master_schema_csn=%s", master_schema_csn)
303 -     log.debug("test_ticket47490_three consumer_schema_csn=%s", consumer_schema_csn)
304 -     assert master_schema_csn == consumer_schema_csn
305 - 
306 -     # Check the error log of the supplier does not contain an error
307 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
308 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
309 -     if res is not None:
310 -         assert False
311 - 
312 - 
313 - def test_ticket47490_four(topology_m1c1):
314 -     """
315 -         Summary: Same OC - extra MUST: Schema is pushed - no error
316 - 
317 -         If supplier schema is again a superset (OC with more MUST), then
318 -         schema is  pushed and there is no message in the error log
319 -         State at startup
320 -             - supplier +masterNewOCA +masterNewOCB +consumerNewOCA
321 -             - consumer +masterNewOCA +masterNewOCB +consumerNewOCA
322 -         Final state
323 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
324 -                        +must=telexnumber
325 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
326 -                        +must=telexnumber
327 - 
328 -     """
329 -     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error")
330 - 
331 -     mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
332 -            new_may=MAY_OLD)
333 - 
334 -     trigger_schema_push(topology_m1c1)
335 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
336 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
337 - 
338 -     # Check the schemaCSN was updated on the consumer
339 -     log.debug("test_ticket47490_four master_schema_csn=%s", master_schema_csn)
340 -     log.debug("ctest_ticket47490_four onsumer_schema_csn=%s", consumer_schema_csn)
341 -     assert master_schema_csn == consumer_schema_csn
342 - 
343 -     # Check the error log of the supplier does not contain an error
344 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
345 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
346 -     if res is not None:
347 -         assert False
348 - 
349 - 
350 - def test_ticket47490_five(topology_m1c1):
351 -     """
352 -         Summary: Same OC - extra MUST: Schema is pushed - (fix for 47721)
353 - 
354 -         If consumer schema is  a superset (OC with more MUST), then
355 -         schema is  pushed (fix for 47721) and there is a message in the error log
356 -         State at startup
357 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA
358 -                        +must=telexnumber
359 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
360 -                         +must=telexnumber
361 -         Final state
362 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
363 -                        +must=telexnumber
364 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
365 -                        +must=telexnumber                   +must=telexnumber
366 - 
367 -         Note: replication log is enabled to get more details
368 -     """
369 -     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - (fix for 47721)")
370 - 
371 -     # get more detail why it fails
372 -     topology_m1c1.ms["master1"].enableReplLogging()
373 - 
374 -     # add telenumber to 'consumerNewOCA' on the consumer
375 -     mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
376 -            new_may=MAY_OLD)
377 -     # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
378 -     time.sleep(2)
379 -     add_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC')
380 - 
381 -     trigger_schema_push(topology_m1c1)
382 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
383 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
384 - 
385 -     # Check the schemaCSN was NOT updated on the consumer
386 -     # with 47721, supplier learns the missing definition
387 -     log.debug("test_ticket47490_five master_schema_csn=%s", master_schema_csn)
388 -     log.debug("ctest_ticket47490_five onsumer_schema_csn=%s", consumer_schema_csn)
389 -     if support_schema_learning(topology_m1c1):
390 -         assert master_schema_csn == consumer_schema_csn
391 -     else:
392 -         assert master_schema_csn != consumer_schema_csn
393 - 
394 -     # Check the error log of the supplier does not contain an error
395 -     # This message may happen during the learning phase
396 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
397 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
398 - 
399 - 
400 - def test_ticket47490_six(topology_m1c1):
401 -     """
402 -         Summary: Same OC - extra MUST: Schema is pushed - no error
403 - 
404 -         If supplier schema is  again a superset (OC with more MUST), then
405 -         schema is  pushed and there is no message in the error log
406 -         State at startup
407 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
408 -                        +must=telexnumber
409 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA
410 -                        +must=telexnumber                   +must=telexnumber
411 -         Final state
412 - 
413 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
414 -                        +must=telexnumber                   +must=telexnumber
415 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
416 -                        +must=telexnumber                   +must=telexnumber
417 - 
418 -         Note: replication log is enabled to get more details
419 -     """
420 -     _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error")
421 - 
422 -     # add telenumber to 'consumerNewOCA' on the consumer
423 -     mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD,
424 -            new_may=MAY_OLD)
425 - 
426 -     trigger_schema_push(topology_m1c1)
427 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
428 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
429 - 
430 -     # Check the schemaCSN was NOT updated on the consumer
431 -     log.debug("test_ticket47490_six master_schema_csn=%s", master_schema_csn)
432 -     log.debug("ctest_ticket47490_six onsumer_schema_csn=%s", consumer_schema_csn)
433 -     assert master_schema_csn == consumer_schema_csn
434 - 
435 -     # Check the error log of the supplier does not contain an error
436 -     # This message may happen during the learning phase
437 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
438 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
439 -     if res is not None:
440 -         assert False
441 - 
442 - 
443 - def test_ticket47490_seven(topology_m1c1):
444 -     """
445 -         Summary: Same OC - extra MAY: Schema is pushed - no error
446 - 
447 -         If supplier schema is again a superset (OC with more MAY), then
448 -         schema is  pushed and there is no message in the error log
449 -         State at startup
450 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
451 -                        +must=telexnumber                   +must=telexnumber
452 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
453 -                        +must=telexnumber                   +must=telexnumber
454 -         Final stat
455 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
456 -                        +must=telexnumber                   +must=telexnumber
457 -                        +may=postOfficeBox
458 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
459 -                        +must=telexnumber                   +must=telexnumber
460 -                        +may=postOfficeBox
461 -     """
462 -     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error")
463 - 
464 -     mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
465 -            new_may=MAY_NEW)
466 - 
467 -     trigger_schema_push(topology_m1c1)
468 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
469 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
470 - 
471 -     # Check the schemaCSN was updated on the consumer
472 -     log.debug("test_ticket47490_seven master_schema_csn=%s", master_schema_csn)
473 -     log.debug("ctest_ticket47490_seven consumer_schema_csn=%s", consumer_schema_csn)
474 -     assert master_schema_csn == consumer_schema_csn
475 - 
476 -     # Check the error log of the supplier does not contain an error
477 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
478 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
479 -     if res is not None:
480 -         assert False
481 - 
482 - 
483 - def test_ticket47490_eight(topology_m1c1):
484 -     """
485 -         Summary: Same OC - extra MAY: Schema is pushed (fix for 47721)
486 - 
487 -         If consumer schema is a superset (OC with more MAY), then
488 -         schema is  pushed (fix for 47721) and there is  message in the error log
489 -         State at startup
490 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
491 -                        +must=telexnumber                   +must=telexnumber
492 -                        +may=postOfficeBox
493 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
494 -                        +must=telexnumber                   +must=telexnumber
495 -                        +may=postOfficeBox
496 -         Final state
497 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
498 -                        +must=telexnumber                   +must=telexnumber
499 -                        +may=postOfficeBox                                     +may=postOfficeBox
500 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
501 -                        +must=telexnumber                   +must=telexnumber
502 -                        +may=postOfficeBox                  +may=postOfficeBox
503 -     """
504 -     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed (fix for 47721)")
505 - 
506 -     mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
507 -            new_may=MAY_NEW)
508 - 
509 -     # modify OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s)
510 -     time.sleep(2)
511 -     mod_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC', old_must=MUST_OLD, new_must=MUST_OLD, old_may=MAY_OLD,
512 -            new_may=MAY_NEW)
513 - 
514 -     trigger_schema_push(topology_m1c1)
515 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
516 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
517 - 
518 -     # Check the schemaCSN was not updated on the consumer
519 -     # with 47721, supplier learns the missing definition
520 -     log.debug("test_ticket47490_eight master_schema_csn=%s", master_schema_csn)
521 -     log.debug("ctest_ticket47490_eight onsumer_schema_csn=%s", consumer_schema_csn)
522 -     if support_schema_learning(topology_m1c1):
523 -         assert master_schema_csn == consumer_schema_csn
524 -     else:
525 -         assert master_schema_csn != consumer_schema_csn
526 - 
527 -     # Check the error log of the supplier does not contain an error
528 -     # This message may happen during the learning phase
529 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
530 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
531 - 
532 - 
533 - def test_ticket47490_nine(topology_m1c1):
534 -     """
535 -         Summary: Same OC - extra MAY: Schema is pushed - no error
536 - 
537 -         If consumer schema is a superset (OC with more MAY), then
538 -         schema is  not pushed and there is  message in the error log
539 -         State at startup
540 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
541 -                        +must=telexnumber                   +must=telexnumber
542 -                        +may=postOfficeBox                                     +may=postOfficeBox
543 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
544 -                        +must=telexnumber                   +must=telexnumber
545 -                        +may=postOfficeBox                  +may=postOfficeBox
546 - 
547 -         Final state
548 - 
549 -             - supplier +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
550 -                        +must=telexnumber                   +must=telexnumber
551 -                        +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
552 -             - consumer +masterNewOCA     +masterNewOCB     +consumerNewOCA    +masterNewOCC
553 -                        +must=telexnumber                   +must=telexnumber
554 -                        +may=postOfficeBox                  +may=postOfficeBox +may=postOfficeBox
555 -     """
556 -     _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error")
557 - 
558 -     mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD,
559 -            new_may=MAY_NEW)
560 - 
561 -     trigger_schema_push(topology_m1c1)
562 -     master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn()
563 -     consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn()
564 - 
565 -     # Check the schemaCSN was updated on the consumer
566 -     log.debug("test_ticket47490_nine master_schema_csn=%s", master_schema_csn)
567 -     log.debug("ctest_ticket47490_nine onsumer_schema_csn=%s", consumer_schema_csn)
568 -     assert master_schema_csn == consumer_schema_csn
569 - 
570 -     # Check the error log of the supplier does not contain an error
571 -     regex = re.compile("must not be overwritten \(set replication log for additional info\)")
572 -     res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex)
573 -     if res is not None:
574 -         assert False
575 - 
576 -     log.info('Testcase PASSED')
577 - 
578 - 
579 - if __name__ == '__main__':
580 -     # Run isolated
581 -     # -s for DEBUG mode
582 -     CURRENT_FILE = os.path.realpath(__file__)
583 -     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -1,311 +0,0 @@
  2 - # --- BEGIN COPYRIGHT BLOCK ---
  3 - # Copyright (C) 2016 Red Hat, Inc.
  4 - # All rights reserved.
  5 - #
  6 - # License: GPL (version 3 or any later version).
  7 - # See LICENSE for details.
  8 - # --- END COPYRIGHT BLOCK ---
  9 - #
 10 - import logging
 11 - 
 12 - import ldap
 13 - import pytest
 14 - from lib389 import Entry
 15 - from lib389._constants import *
 16 - from lib389.topologies import topology_st
 17 - 
 18 - log = logging.getLogger(__name__)
 19 - 
 20 - from lib389.utils import *
 21 - 
 22 - # Skip on older versions
 23 - pytestmark = pytest.mark.skipif(ds_is_older('1.3.2'), reason="Not implemented")
 24 - OC_NAME = 'OCticket47653'
 25 - MUST = "(postalAddress $ postalCode)"
 26 - MAY = "(member $ street)"
 27 - 
 28 - OTHER_NAME = 'other_entry'
 29 - MAX_OTHERS = 10
 30 - 
 31 - BIND_NAME = 'bind_entry'
 32 - BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX)
 33 - BIND_PW = 'password'
 34 - 
 35 - ENTRY_NAME = 'test_entry'
 36 - ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX)
 37 - ENTRY_OC = "top person %s" % OC_NAME
 38 - 
 39 - 
 40 - def _oc_definition(oid_ext, name, must=None, may=None):
 41 -     oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext
 42 -     desc = 'To test ticket 47490'
 43 -     sup = 'person'
 44 -     if not must:
 45 -         must = MUST
 46 -     if not may:
 47 -         may = MAY
 48 - 
 49 -     new_oc = "( %s  NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may)
 50 -     return new_oc
 51 - 
 52 - 
 53 - def test_ticket47653_init(topology_st):
 54 -     """
 55 -         It adds
 56 -            - Objectclass with MAY 'member'
 57 -            - an entry ('bind_entry') with which we bind to test the 'SELFDN' operation
 58 -         It deletes the anonymous aci
 59 - 
 60 -     """
 61 - 
 62 -     topology_st.standalone.log.info("Add %s that allows 'member' attribute" % OC_NAME)
 63 -     new_oc = _oc_definition(2, OC_NAME, must=MUST, may=MAY)
 64 -     topology_st.standalone.schema.add_schema('objectClasses', new_oc)
 65 - 
 66 -     # entry used to bind with
 67 -     topology_st.standalone.log.info("Add %s" % BIND_DN)
 68 -     topology_st.standalone.add_s(Entry((BIND_DN, {
 69 -         'objectclass': "top person".split(),
 70 -         'sn': BIND_NAME,
 71 -         'cn': BIND_NAME,
 72 -         'userpassword': BIND_PW})))
 73 - 
 74 -     # enable acl error logging
 75 -     mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')]
 76 -     topology_st.standalone.modify_s(DN_CONFIG, mod)
 77 - 
 78 -     # Remove aci's to start with a clean slate
 79 -     mod = [(ldap.MOD_DELETE, 'aci', None)]
 80 -     topology_st.standalone.modify_s(SUFFIX, mod)
 81 - 
 82 -     # add dummy entries
 83 -     for cpt in range(MAX_OTHERS):
 84 -         name = "%s%d" % (OTHER_NAME, cpt)
 85 -         topology_st.standalone.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
 86 -             'objectclass': "top person".split(),
 87 -             'sn': name,
 88 -             'cn': name})))
 89 - 
 90 - 
 91 - def test_ticket47653_add(topology_st):
 92 -     '''
 93 -         It checks that, bound as bind_entry,
 94 -             - we can not ADD an entry without the proper SELFDN aci.
 95 -             - with the proper ACI we can not ADD with 'member' attribute
 96 -             - with the proper ACI and 'member' it succeeds to ADD
 97 -     '''
 98 -     topology_st.standalone.log.info("\n\n######################### ADD ######################\n")
 99 - 
100 -     # bind as bind_entry
101 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
102 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
103 - 
104 -     # Prepare the entry with multivalued members
105 -     entry_with_members = Entry(ENTRY_DN)
106 -     entry_with_members.setValues('objectclass', 'top', 'person', 'OCticket47653')
107 -     entry_with_members.setValues('sn', ENTRY_NAME)
108 -     entry_with_members.setValues('cn', ENTRY_NAME)
109 -     entry_with_members.setValues('postalAddress', 'here')
110 -     entry_with_members.setValues('postalCode', '1234')
111 -     members = []
112 -     for cpt in range(MAX_OTHERS):
113 -         name = "%s%d" % (OTHER_NAME, cpt)
114 -         members.append("cn=%s,%s" % (name, SUFFIX))
115 -     members.append(BIND_DN)
116 -     entry_with_members.setValues('member', members)
117 - 
118 -     # Prepare the entry with one member
119 -     entry_with_member = Entry(ENTRY_DN)
120 -     entry_with_member.setValues('objectclass', 'top', 'person', 'OCticket47653')
121 -     entry_with_member.setValues('sn', ENTRY_NAME)
122 -     entry_with_member.setValues('cn', ENTRY_NAME)
123 -     entry_with_member.setValues('postalAddress', 'here')
124 -     entry_with_member.setValues('postalCode', '1234')
125 -     member = []
126 -     member.append(BIND_DN)
127 -     entry_with_member.setValues('member', member)
128 - 
129 -     # entry to add WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
130 -     try:
131 -         topology_st.standalone.log.info("Try to add Add  %s (aci is missing): %r" % (ENTRY_DN, entry_with_member))
132 - 
133 -         topology_st.standalone.add_s(entry_with_member)
134 -     except Exception as e:
135 -         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
136 -         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
137 - 
138 -     # Ok Now add the proper ACI
139 -     topology_st.standalone.log.info("Bind as %s and add the ADD SELFDN aci" % DN_DM)
140 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
141 - 
142 -     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
143 -     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
144 -     ACI_ALLOW = "(version 3.0; acl \"SelfDN add\"; allow (add)"
145 -     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
146 -     ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
147 -     mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
148 -     topology_st.standalone.modify_s(SUFFIX, mod)
149 - 
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 - 
154 -     # entry to add WITHOUT member and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
155 -     try:
156 -         topology_st.standalone.log.info("Try to add Add  %s (member is missing)" % ENTRY_DN)
157 -         topology_st.standalone.add_s(Entry((ENTRY_DN, {
158 -             'objectclass': ENTRY_OC.split(),
159 -             'sn': ENTRY_NAME,
160 -             'cn': ENTRY_NAME,
161 -             'postalAddress': 'here',
162 -             'postalCode': '1234'})))
163 -     except Exception as e:
164 -         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
165 -         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
166 - 
167 -     # entry to add WITH memberS and WITH the ACI -> ldap.INSUFFICIENT_ACCESS
168 -     # member should contain only one value
169 -     try:
170 -         topology_st.standalone.log.info("Try to add Add  %s (with several member values)" % ENTRY_DN)
171 -         topology_st.standalone.add_s(entry_with_members)
172 -     except Exception as e:
173 -         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
174 -         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
175 - 
176 -     topology_st.standalone.log.info("Try to add Add  %s should be successful" % ENTRY_DN)
177 -     topology_st.standalone.add_s(entry_with_member)
178 - 
179 - 
180 - def test_ticket47653_search(topology_st):
181 -     '''
182 -         It checks that, bound as bind_entry,
183 -             - we can not search an entry without the proper SELFDN aci.
184 -             - adding the ACI, we can search the entry
185 -     '''
186 -     topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n")
187 -     # bind as bind_entry
188 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
189 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
190 - 
191 -     # entry to search WITH member being BIND_DN but WITHOUT the ACI -> no entry returned
192 -     topology_st.standalone.log.info("Try to search  %s (aci is missing)" % ENTRY_DN)
193 -     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
194 -     assert len(ents) == 0
195 - 
196 -     # Ok Now add the proper ACI
197 -     topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
198 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
199 - 
200 -     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
201 -     ACI_TARGETATTR = "(targetattr = *)"
202 -     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
203 -     ACI_ALLOW = "(version 3.0; acl \"SelfDN search-read\"; allow (read, search, compare)"
204 -     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
205 -     ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
206 -     mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
207 -     topology_st.standalone.modify_s(SUFFIX, mod)
208 - 
209 -     # bind as bind_entry
210 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
211 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
212 - 
213 -     # entry to search with the proper aci
214 -     topology_st.standalone.log.info("Try to search  %s should be successful" % ENTRY_DN)
215 -     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
216 -     assert len(ents) == 1
217 - 
218 - 
219 - def test_ticket47653_modify(topology_st):
220 -     '''
221 -         It checks that, bound as bind_entry,
222 -             - we can not modify an entry without the proper SELFDN aci.
223 -             - adding the ACI, we can modify the entry
224 -     '''
225 -     # bind as bind_entry
226 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
227 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
228 - 
229 -     topology_st.standalone.log.info("\n\n######################### MODIFY ######################\n")
230 - 
231 -     # entry to modify WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
232 -     try:
233 -         topology_st.standalone.log.info("Try to modify  %s (aci is missing)" % ENTRY_DN)
234 -         mod = [(ldap.MOD_REPLACE, 'postalCode', '9876')]
235 -         topology_st.standalone.modify_s(ENTRY_DN, mod)
236 -     except Exception as e:
237 -         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
238 -         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
239 - 
240 -     # Ok Now add the proper ACI
241 -     topology_st.standalone.log.info("Bind as %s and add the WRITE SELFDN aci" % DN_DM)
242 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
243 - 
244 -     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
245 -     ACI_TARGETATTR = "(targetattr = *)"
246 -     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
247 -     ACI_ALLOW = "(version 3.0; acl \"SelfDN write\"; allow (write)"
248 -     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
249 -     ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
250 -     mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
251 -     topology_st.standalone.modify_s(SUFFIX, mod)
252 - 
253 -     # bind as bind_entry
254 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
255 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
256 - 
257 -     # modify the entry and checks the value
258 -     topology_st.standalone.log.info("Try to modify  %s. It should succeeds" % ENTRY_DN)
259 -     mod = [(ldap.MOD_REPLACE, 'postalCode', '1928')]
260 -     topology_st.standalone.modify_s(ENTRY_DN, mod)
261 - 
262 -     ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*')
263 -     assert len(ents) == 1
264 -     assert ents[0].postalCode == '1928'
265 - 
266 - 
267 - def test_ticket47653_delete(topology_st):
268 -     '''
269 -         It checks that, bound as bind_entry,
270 -             - we can not delete an entry without the proper SELFDN aci.
271 -             - adding the ACI, we can delete the entry
272 -     '''
273 -     topology_st.standalone.log.info("\n\n######################### DELETE ######################\n")
274 - 
275 -     # bind as bind_entry
276 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
277 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
278 - 
279 -     # entry to delete WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS
280 -     try:
281 -         topology_st.standalone.log.info("Try to delete  %s (aci is missing)" % ENTRY_DN)
282 -         topology_st.standalone.delete_s(ENTRY_DN)
283 -     except Exception as e:
284 -         topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__)
285 -         assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
286 - 
287 -     # Ok Now add the proper ACI
288 -     topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM)
289 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
290 - 
291 -     ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX
292 -     ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME
293 -     ACI_ALLOW = "(version 3.0; acl \"SelfDN delete\"; allow (delete)"
294 -     ACI_SUBJECT = " userattr = \"member#selfDN\";)"
295 -     ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT
296 -     mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
297 -     topology_st.standalone.modify_s(SUFFIX, mod)
298 - 
299 -     # bind as bind_entry
300 -     topology_st.standalone.log.info("Bind as %s" % BIND_DN)
301 -     topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW)
302 - 
303 -     # entry to search with the proper aci
304 -     topology_st.standalone.log.info("Try to delete  %s should be successful" % ENTRY_DN)
305 -     topology_st.standalone.delete_s(ENTRY_DN)
306 - 
307 - 
308 - if __name__ == '__main__':
309 -     # Run isolated
310 -     # -s for DEBUG mode
311 -     CURRENT_FILE = os.path.realpath(__file__)
312 -     pytest.main("-s %s" % CURRENT_FILE)
  1 @@ -1,190 +0,0 @@
  2 - # --- BEGIN COPYRIGHT BLOCK ---
  3 - # Copyright (C) 2016 Red Hat, Inc.
  4 - # All rights reserved.
  5 - #
  6 - # License: GPL (version 3 or any later version).
  7 - # See LICENSE for details.
  8 - # --- END COPYRIGHT BLOCK ---
  9 - #
 10 - import logging
 11 - 
 12 - import pytest
 13 - from lib389.tasks import *
 14 - from lib389.topologies import topology_st
 15 - 
 16 - from lib389._constants import DN_DM, PASSWORD, DEFAULT_SUFFIX
 17 - 
 18 - log = logging.getLogger(__name__)
 19 - 
 20 - CHANGELOG = 'cn=changelog5,cn=config'
 21 - RETROCHANGELOG = 'cn=Retro Changelog Plugin,cn=plugins,cn=config'
 22 - 
 23 - MAXAGE = 'nsslapd-changelogmaxage'
 24 - TRIMINTERVAL = 'nsslapd-changelogtrim-interval'
 25 - COMPACTDBINTERVAL = 'nsslapd-changelogcompactdb-interval'
 26 - 
 27 - FILTER = '(cn=*)'
 28 - 
 29 - 
 30 - def test_ticket47669_init(topology_st):
 31 -     """
 32 -     Add cn=changelog5,cn=config
 33 -     Enable cn=Retro Changelog Plugin,cn=plugins,cn=config
 34 -     """
 35 -     log.info('Testing Ticket 47669 - Test duration syntax in the changelogs')
 36 - 
 37 -     # bind as directory manager
 38 -     topology_st.standalone.log.info("Bind as %s" % DN_DM)
 39 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
 40 - 
 41 -     try:
 42 -         changelogdir = os.path.join(os.path.dirname(topology_st.standalone.dbdir), 'changelog')
 43 -         topology_st.standalone.add_s(Entry((CHANGELOG,
 44 -                                             {'objectclass': 'top extensibleObject'.split(),
 45 -                                              'nsslapd-changelogdir': changelogdir})))
 46 -     except ldap.LDAPError as e:
 47 -         log.error('Failed to add ' + CHANGELOG + ': error ' + e.message['desc'])
 48 -         assert False
 49 - 
 50 -     try:
 51 -         topology_st.standalone.modify_s(RETROCHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
 52 -     except ldap.LDAPError as e:
 53 -         log.error('Failed to enable ' + RETROCHANGELOG + ': error ' + e.message['desc'])
 54 -         assert False
 55 - 
 56 -     # restart the server
 57 -     topology_st.standalone.restart(timeout=10)
 58 - 
 59 - 
 60 - def add_and_check(topology_st, plugin, attr, val, isvalid):
 61 -     """
 62 -     Helper function to add/replace attr: val and check the added value
 63 -     """
 64 -     if isvalid:
 65 -         log.info('Test %s: %s -- valid' % (attr, val))
 66 -         try:
 67 -             topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)])
 68 -         except ldap.LDAPError as e:
 69 -             log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error ' + e.message['desc'])
 70 -             assert False
 71 -     else:
 72 -         log.info('Test %s: %s -- invalid' % (attr, val))
 73 -         if plugin == CHANGELOG:
 74 -             try:
 75 -                 topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)])
 76 -             except ldap.LDAPError as e:
 77 -                 log.error('Expectedly failed to add ' + attr + ': ' + val +
 78 -                           ' to ' + plugin + ': error ' + e.message['desc'])
 79 -         else:
 80 -             try:
 81 -                 topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)])
 82 -             except ldap.LDAPError as e:
 83 -                 log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error ' + e.message['desc'])
 84 - 
 85 -     try:
 86 -         entries = topology_st.standalone.search_s(plugin, ldap.SCOPE_BASE, FILTER, [attr])
 87 -         if isvalid:
 88 -             if not entries[0].hasValue(attr, val):
 89 -                 log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val))
 90 -                 assert False
 91 -         else:
 92 -             if plugin == CHANGELOG:
 93 -                 if entries[0].hasValue(attr, val):
 94 -                     log.fatal('%s has unexpected (%s: %s)' % (plugin, attr, val))
 95 -                     assert False
 96 -             else:
 97 -                 if not entries[0].hasValue(attr, val):
 98 -                     log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val))
 99 -                     assert False
100 -     except ldap.LDAPError as e:
101 -         log.fatal('Unable to search for entry %s: error %s' % (plugin, e.message['desc']))
102 -         assert False
103 - 
104 - 
105 - def test_ticket47669_changelog_maxage(topology_st):
106 -     """
107 -     Test nsslapd-changelogmaxage in cn=changelog5,cn=config
108 -     """
109 -     log.info('1. Test nsslapd-changelogmaxage in cn=changelog5,cn=config')
110 - 
111 -     # bind as directory manager
112 -     topology_st.standalone.log.info("Bind as %s" % DN_DM)
113 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
114 - 
115 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '12345', True)
116 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '10s', True)
117 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '30M', True)
118 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '12h', True)
119 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '2D', True)
120 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '4w', True)
121 -     add_and_check(topology_st, CHANGELOG, MAXAGE, '-123', False)
122 -     add_and_check(topology_st, CHANGELOG, MAXAGE, 'xyz', False)
123 - 
124 - 
125 - def test_ticket47669_changelog_triminterval(topology_st):
126 -     """
127 -     Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config
128 -     """
129 -     log.info('2. Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config')
130 - 
131 -     # bind as directory manager
132 -     topology_st.standalone.log.info("Bind as %s" % DN_DM)
133 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
134 - 
135 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '12345', True)
136 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '10s', True)
137 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '30M', True)
138 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '12h', True)
139 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '2D', True)
140 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '4w', True)
141 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '-123', False)
142 -     add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, 'xyz', False)
143 - 
144 - 
145 - def test_ticket47669_changelog_compactdbinterval(topology_st):
146 -     """
147 -     Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config
148 -     """
149 -     log.info('3. Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config')
150 - 
151 -     # bind as directory manager
152 -     topology_st.standalone.log.info("Bind as %s" % DN_DM)
153 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
154 - 
155 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '12345', True)
156 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '10s', True)
157 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '30M', True)
158 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '12h', True)
159 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '2D', True)
160 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '4w', True)
161 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '-123', False)
162 -     add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, 'xyz', False)
163 - 
164 - 
165 - def test_ticket47669_retrochangelog_maxage(topology_st):
166 -     """
167 -     Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config
168 -     """
169 -     log.info('4. Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config')
170 - 
171 -     # bind as directory manager
172 -     topology_st.standalone.log.info("Bind as %s" % DN_DM)
173 -     topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
174 - 
175 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '12345', True)
176 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '10s', True)
177 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '30M', True)
178 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '12h', True)
179 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '2D', True)
180 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '4w', True)
181 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '-123', False)
182 -     add_and_check(topology_st, RETROCHANGELOG, MAXAGE, 'xyz', False)
183 - 
184 -     topology_st.standalone.log.info("ticket47669 was successfully verified.")
185 - 
186 - 
187 - if __name__ == '__main__':
188 -     # Run isolated
189 -     # -s for DEBUG mode
190 -     CURRENT_FILE = os.path.realpath(__file__)
191 -     pytest.main("-s %s" % CURRENT_FILE)