def basic_authz(sbox): "verify that unreadable content is not synced" sbox.build(create_wc=False) write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) run_init(dest_sbox.repo_url, sbox.repo_url) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={ src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_sync(dest_sbox.repo_url) lambda_url = dest_sbox.repo_url + '/A/B/lambda' iota_url = dest_sbox.repo_url + '/iota' # this file should have been blocked by authz svntest.actions.run_and_verify_svn([], svntest.verify.AnyOutput, 'cat', lambda_url) # this file should have been synced svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], 'cat', iota_url)
def authz_recursive_ls(sbox): "recursive ls with private subtrees" sbox.build(create_wc=False) local_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, { '/': '* = r', '/A/B/E': '* =', '/A/mu': '* =', }) expected_entries = [ 'A/', 'A/B/', 'A/B/F/', 'A/B/lambda', 'A/C/', 'A/D/', 'A/D/G/', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H/', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', 'A/D/gamma', 'iota', ] svntest.actions.run_and_verify_svn( 'recursive ls from /', map(lambda x: x + '\n', expected_entries), [], 'ls', '-R', sbox.repo_url)
def copy_delete_unreadable_child(sbox): "copy, then rm at-src-unreadable child" # Prepare the source: Greek tree (r1), cp+rm (r2). sbox.build(create_wc = False) svntest.actions.run_and_verify_svnmucc(None, [], '-m', 'r2', '-U', sbox.repo_url, 'cp', 'HEAD', '/', 'branch', 'rm', 'branch/A') # Create the destination. dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) # Lock down the source. write_restrictive_svnserve_conf(sbox.repo_dir, anon_access='read') src_authz = sbox.authz_name() write_authz_file(sbox, None, prefixed_rules = { src_authz + ':/': '* = r', src_authz + ':/A': '* =', }) dest_url = dest_sbox.file_protocol_repo_url() run_init(dest_url, sbox.repo_url) run_sync(dest_url) # sanity check svntest.actions.run_and_verify_svn(["iota\n"], [], 'ls', dest_url+'/branch@2')
def authz_partial_export_test(sbox): "test authz for export with unreadable subfolder" sbox.build(create_wc = False, read_only = True) local_dir = sbox.wc_dir # cleanup remains of a previous test run. svntest.main.safe_rmtree(local_dir) write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable read access on folder A/B, export should not # download this folder # write an authz file with *= on /A/B write_authz_file(sbox, { "/": "* = r", "/A/B": "* =" }) # export a working copy, should not dl /A/B expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.desc[''] = Item() expected_output.tweak(status='A ', contents=None) expected_output.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') expected_wc = svntest.main.greek_state.copy() expected_wc.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') svntest.actions.run_and_verify_export(sbox.repo_url, local_dir, expected_output, expected_wc)
def basic_authz(sbox): "verify that unreadable content is not synced" sbox.build(create_wc=False) write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) run_init(dest_sbox.repo_url, sbox.repo_url) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file( sbox, None, prefixed_rules={src_authz + ":/": "* = r", src_authz + ":/A/B": "* =", dst_authz + ":/": "* = rw"} ) run_sync(dest_sbox.repo_url) lambda_url = dest_sbox.repo_url + "/A/B/lambda" iota_url = dest_sbox.repo_url + "/iota" # this file should have been blocked by authz svntest.actions.run_and_verify_svn([], svntest.verify.AnyOutput, "cat", lambda_url) # this file should have been synced svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "cat", iota_url)
def authz_aliases(sbox): "test authz for aliases" sbox.build(create_wc=False) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, { "/": "* = r", "/A/B": "&jray = rw" }, {"aliases": 'jray = jrandom'}) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' iota_url = root_url + '/iota' # copy a remote file, target is readonly for jconstant: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '--username', svntest.main.wc_author2, '-m', 'logmsg', iota_url, B_url) # try the same action, but as user jray (alias of jrandom), should work. svntest.actions.run_and_verify_svn(None, None, [], 'cp', '-m', 'logmsg', iota_url, B_url)
def authz_open_root(sbox): "authz issue #2486 - open root" sbox.build() write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) # we have write access in folder /A, but not in root. Test on too # restrictive access needed in open_root by modifying a file in /A wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') svntest.main.file_append(mu_path, "hi") # Create expected output tree. expected_output = svntest.wc.State(wc_dir, { 'A/mu' : Item(verb='Sending'), }) # Commit the one file. svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None, mu_path)
def copy_delete_unreadable_child(sbox): "copy, then rm at-src-unreadable child" # Prepare the source: Greek tree (r1), cp+rm (r2). sbox.build(create_wc=False) svntest.actions.run_and_verify_svnmucc( None, [], "-m", "r2", "-U", sbox.repo_url, "cp", "HEAD", "/", "branch", "rm", "branch/A" ) # Create the destination. dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) # Lock down the source. write_restrictive_svnserve_conf(sbox.repo_dir, anon_access="read") src_authz = sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={src_authz + ":/": "* = r", src_authz + ":/A": "* ="}) dest_url = dest_sbox.file_protocol_repo_url() run_init(dest_url, sbox.repo_url) run_sync(dest_url) # sanity check svntest.actions.run_and_verify_svn(["iota\n"], [], "ls", dest_url + "/branch@2")
def authz_open_directory(sbox): "authz issue #2486 - open directory" sbox.build() write_authz_file(sbox, {"/": "*=rw", "/A/B": "*=", "/A/B/E": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) # we have write access in folder /A/B/E, but not in /A/B. Test on too # restrictive access needed in open_directory by moving file /A/mu to # /A/B/E wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') E_path = os.path.join(wc_dir, 'A', 'B', 'E') svntest.main.run_svn(None, 'mv', mu_path, E_path) # Create expected output tree. expected_output = svntest.wc.State(wc_dir, { 'A/mu' : Item(verb='Deleting'), 'A/B/E/mu' : Item(verb='Adding'), }) # Commit the working copy. svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None, wc_dir)
def authz_aliases(sbox): "test authz for aliases" sbox.build(create_wc = False) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, { "/" : "* = r", "/A/B" : "&jray = rw" }, { "aliases" : 'jray = jrandom' } ) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' iota_url = root_url + '/iota' # copy a remote file, target is readonly for jconstant: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '--username', svntest.main.wc_author2, '-m', 'logmsg', iota_url, B_url) # try the same action, but as user jray (alias of jrandom), should work. svntest.actions.run_and_verify_svn(None, None, [], 'cp', '-m', 'logmsg', iota_url, B_url)
def authz_access_required_at_repo_root2(sbox): "more authz issue #3242 - update to renamed file" sbox.build(create_wc=False) root_url = sbox.repo_url # Now we get all restrictive. write_authz_file(sbox, {'/': '* =', '/A': 'jrandom = rw'}) write_restrictive_svnserve_conf(sbox.repo_dir) # Rename a file. svntest.main.run_svn(None, 'mv', '-m', 'rename file in readable writable space', root_url + '/A/B/E/alpha', root_url + '/A/B/E/alpha-renamed') # Check out original greek sub tree below /A/B/E # and update it to the above rename. wc_dir = sbox.add_wc_path('ABE') os.mkdir(wc_dir) svntest.main.run_svn(None, 'co', '-r', '1', root_url + '/A/B/E', wc_dir) svntest.main.run_svn(None, 'up', wc_dir) # Rename a directory. svntest.main.run_svn(None, 'mv', '-m', 'rename diretory in readable writable space', root_url + '/A/D/H', root_url + '/A/D/a g e') # Check out original greek sub tree below /A/D # and update it to the above rename. wc_dir = sbox.add_wc_path('AD') os.mkdir(wc_dir) svntest.main.run_svn(None, 'co', '-r', '1', root_url + '/A/D', wc_dir) svntest.main.run_svn(None, 'up', wc_dir)
def authz_checkout_test(sbox): "test authz for checkout" sbox.build(create_wc=False, read_only=True) local_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable all read access, checkout should fail # write an authz file with *= on / if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, {"/": "* ="}) # checkout a working copy, should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'co', sbox.repo_url, local_dir) # 2nd part: now enable read access write_authz_file(sbox, {"/": "* = r"}) # checkout a working copy, should succeed because we have read access expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.tweak(status='A ', contents=None) expected_wc = svntest.main.greek_state svntest.actions.run_and_verify_checkout(sbox.repo_url, local_dir, expected_output, expected_wc)
def authz_partial_export_test(sbox): "test authz for export with unreadable subfolder" sbox.build(create_wc=False, read_only=True) local_dir = sbox.wc_dir # cleanup remains of a previous test run. svntest.main.safe_rmtree(local_dir) write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable read access on folder A/B, export should not # download this folder # write an authz file with *= on /A/B write_authz_file(sbox, {"/": "* = r", "/A/B": "* ="}) # export a working copy, should not dl /A/B expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.desc[''] = Item() expected_output.tweak(status='A ', contents=None) expected_output.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') expected_wc = svntest.main.greek_state.copy() expected_wc.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') svntest.actions.run_and_verify_export(sbox.repo_url, local_dir, expected_output, expected_wc)
def copy_delete_unreadable_child(sbox): "copy, then rm at-src-unreadable child" # Prepare the source: Greek tree (r1), cp+rm (r2). sbox.build(create_wc=False) svntest.actions.run_and_verify_svnmucc(None, [], '-m', 'r2', '-U', sbox.repo_url, 'cp', 'HEAD', '/', 'branch', 'rm', 'branch/A') # Create the destination. dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) # Lock down the source. write_restrictive_svnserve_conf(sbox.repo_dir, anon_access='read') src_authz = sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={ src_authz + ':/': '* = r', src_authz + ':/A': '* =', }) dest_url = dest_sbox.file_protocol_repo_url() run_init(dest_url, sbox.repo_url) run_sync(dest_url) # sanity check svntest.actions.run_and_verify_svn(["iota\n"], [], 'ls', dest_url + '/branch@2')
def authz_open_directory(sbox): "authz issue #2486 - open directory" sbox.build() write_authz_file(sbox, { "/": "*=rw", "/A/B": "*=", "/A/B/E": "jrandom = rw" }) write_restrictive_svnserve_conf(sbox.repo_dir) # we have write access in folder /A/B/E, but not in /A/B. Test on too # restrictive access needed in open_directory by moving file /A/mu to # /A/B/E wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') E_path = os.path.join(wc_dir, 'A', 'B', 'E') svntest.main.run_svn(None, 'mv', mu_path, E_path) # Create expected output tree. expected_output = svntest.wc.State(wc_dir, { 'A/mu': Item(verb='Deleting'), 'A/B/E/mu': Item(verb='Adding'), }) # Commit the working copy. svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None, wc_dir)
def authz_write_access(sbox): "test authz for write operations" sbox.build(create_wc=False) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Access denied.*" write_authz_file(sbox, {"/": "* = r", "/A/B": "* = rw", "/A/C": "* = rw"}) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' C_url = A_url + '/C' E_url = B_url + '/E' mu_url = A_url + '/mu' iota_url = root_url + '/iota' lambda_url = B_url + '/lambda' D_url = A_url + '/D' # copy a remote file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', lambda_url, D_url) # copy a remote folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', E_url, D_url) # delete a file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'rm', '-m', 'logmsg', iota_url) # delete a folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'rm', '-m', 'logmsg', D_url) # create a folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mkdir', '-m', 'logmsg', A_url + '/newfolder') # move a remote file, source is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', mu_url, C_url) # move a remote folder, source is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', D_url, C_url) # move a remote file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', lambda_url, D_url) # move a remote folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', B_url, D_url)
def copy_from_unreadable_dir(sbox): "verify that copies from unreadable dirs work" sbox.build() B_url = sbox.repo_url + "/A/B" P_url = sbox.repo_url + "/A/P" # Set a property on the directory we're going to copy, and a file in it, to # confirm that they're transmitted when we later sync the copied directory svntest.actions.run_and_verify_svn(None, [], "pset", "foo", "bar", sbox.wc_dir + "/A/B/lambda") svntest.actions.run_and_verify_svn(None, [], "pset", "baz", "zot", sbox.wc_dir + "/A/B") svntest.actions.run_and_verify_svn(None, [], "ci", sbox.wc_dir + "/A/B", "-m", "log_msg") # Now copy that directory so we'll see it in our synced copy svntest.actions.run_and_verify_svn(None, [], "cp", B_url, P_url, "-m", "Copy B to P") write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file( sbox, None, prefixed_rules={src_authz + ":/": "* = r", src_authz + ":/A/B": "* =", dst_authz + ":/": "* = rw"} ) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ "Changed paths:\n", " A /A/P\n", " A /A/P/E\n", " A /A/P/E/alpha\n", " A /A/P/E/beta\n", " A /A/P/F\n", " A /A/P/lambda\n", "\n", "\n", # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, "log", "-r", "3", "-v", dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, "LOG", expected_out, out[2:11]) svntest.actions.run_and_verify_svn(["bar\n"], [], "pget", "foo", dest_sbox.repo_url + "/A/P/lambda") svntest.actions.run_and_verify_svn(["zot\n"], [], "pget", "baz", dest_sbox.repo_url + "/A/P")
def authz_checkout_and_update_test(sbox): "test authz for checkout and update" sbox.build(create_wc = False, read_only = True) local_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable read access on folder A/B, checkout should not # download this folder # write an authz file with *= on /A/B write_authz_file(sbox, { "/": "* = r", "/A/B": "* ="}) # checkout a working copy, should not dl /A/B expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.tweak(status='A ', contents=None) expected_output.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') expected_wc = svntest.main.greek_state.copy() expected_wc.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') svntest.actions.run_and_verify_checkout(sbox.repo_url, local_dir, expected_output, expected_wc) # 2nd part: now enable read access # write an authz file with *=r on / write_authz_file(sbox, { "/": "* = r"}) # update the working copy, should download /A/B because we now have read # access expected_output = svntest.wc.State(local_dir, { 'A/B' : Item(status='A '), 'A/B/lambda' : Item(status='A '), 'A/B/E' : Item(status='A '), 'A/B/E/alpha' : Item(status='A '), 'A/B/E/beta' : Item(status='A '), 'A/B/F' : Item(status='A '), }) expected_wc = svntest.main.greek_state expected_status = svntest.actions.get_virginal_state(local_dir, 1) svntest.actions.run_and_verify_update(local_dir, expected_output, expected_wc, expected_status, None, None, None, None, None, 1)
def authz_locking(sbox): "test authz for locking" sbox.build() write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" root_url = sbox.repo_url wc_dir = sbox.wc_dir iota_url = root_url + '/iota' iota_path = os.path.join(wc_dir, 'iota') A_url = root_url + '/A' mu_path = os.path.join(wc_dir, 'A', 'mu') # lock a file url, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'lock', '-m', 'lock msg', iota_url) # lock a file path, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'lock', '-m', 'lock msg', iota_path) # Test for issue 2700: we have write access in folder /A, but not in root. # Get a lock on /A/mu and try to commit it. # lock a file path, target is writeable: should succeed svntest.actions.run_and_verify_svn(None, None, [], 'lock', '-m', 'lock msg', mu_path) svntest.main.file_append(mu_path, "hi") expected_output = svntest.wc.State(wc_dir, { 'A/mu' : Item(verb='Sending'), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, [], None, mu_path)
def authz_checkout_and_update_test(sbox): "test authz for checkout and update" sbox.build(create_wc=False, read_only=True) local_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable read access on folder A/B, checkout should not # download this folder # write an authz file with *= on /A/B write_authz_file(sbox, {"/": "* = r", "/A/B": "* ="}) # checkout a working copy, should not dl /A/B expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.tweak(status='A ', contents=None) expected_output.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') expected_wc = svntest.main.greek_state.copy() expected_wc.remove('A/B', 'A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F') svntest.actions.run_and_verify_checkout(sbox.repo_url, local_dir, expected_output, expected_wc) # 2nd part: now enable read access # write an authz file with *=r on / write_authz_file(sbox, {"/": "* = r"}) # update the working copy, should download /A/B because we now have read # access expected_output = svntest.wc.State( local_dir, { 'A/B': Item(status='A '), 'A/B/lambda': Item(status='A '), 'A/B/E': Item(status='A '), 'A/B/E/alpha': Item(status='A '), 'A/B/E/beta': Item(status='A '), 'A/B/F': Item(status='A '), }) expected_wc = svntest.main.greek_state expected_status = svntest.actions.get_virginal_state(local_dir, 1) svntest.actions.run_and_verify_update(local_dir, expected_output, expected_wc, expected_status, None, None, None, None, None, 1)
def copy_with_mod_from_unreadable_dir_and_copy(sbox): "verify copies with mods from unreadable dirs +copy" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], "cp", sbox.wc_dir + "/A/B", sbox.wc_dir + "/A/P") # Copy a (readable) file into the copied directory. svntest.actions.run_and_verify_svn(None, [], "cp", sbox.wc_dir + "/A/D/gamma", sbox.wc_dir + "/A/P/E") # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], "ci", sbox.wc_dir, "-m", "log_msg") # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file( sbox, None, prefixed_rules={src_authz + ":/": "* = r", src_authz + ":/A/B": "* =", dst_authz + ":/": "* = rw"} ) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ "Changed paths:\n", " A /A/P\n", " A /A/P/E\n", " A /A/P/E/alpha\n", " A /A/P/E/beta\n", " A /A/P/E/gamma (from /A/D/gamma:1)\n", " A /A/P/F\n", " A /A/P/lambda\n", "\n", "\n", # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, "log", "-r", "2", "-v", dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, "LOG", expected_out, out[2:12])
def authz_svnserve_anon_access_read(sbox): "authz issue #2712" sbox.build(create_wc=False) svntest.main.safe_rmtree(sbox.wc_dir) B_path = os.path.join(sbox.wc_dir, 'A', 'B') other_B_path = B_path + '_other' B_url = sbox.repo_url + '/A/B' D_path = os.path.join(sbox.wc_dir, 'A', 'D') D_url = sbox.repo_url + '/A/D' # We want a svnserve.conf with anon-access = read. write_restrictive_svnserve_conf(sbox.repo_dir, "read") # Give jrandom read access to /A/B. Anonymous users can only # access /A/D. write_authz_file(sbox, {"/A/B": "jrandom = rw", "/A/D": "* = r"}) # Perform a checkout of /A/B, expecting to see no errors. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', B_url, B_path) # Anonymous users should be able to check out /A/D. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', D_url, D_path) # Now try a switch. svntest.main.safe_rmtree(D_path) svntest.actions.run_and_verify_svn(None, None, [], 'switch', D_url, B_path) # Check out /A/B with an unknown username, expect error. svntest.actions.run_and_verify_svn( None, None, ".*Authentication error from server: Username not found.*", 'checkout', '--non-interactive', '--username', 'losing_user', B_url, B_path + '_unsuccessful') # Check out a second copy of /A/B, make changes for later merge. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', B_url, other_B_path) other_alpha_path = os.path.join(other_B_path, 'E', 'alpha') svntest.main.file_append(other_alpha_path, "fish\n") svntest.actions.run_and_verify_svn(None, None, [], 'commit', '-m', 'log msg', other_B_path) # Now try to merge. This is an atypical merge, since our "branch" # is not really a branch (it's the same URL), but we only care about # authz here, not the semantics of the merge. (Merges had been # failing in authz, for the reasons summarized in # http://subversion.tigris.org/issues/show_bug.cgi?id=2712#desc13.) svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '2', B_url, B_path)
def specific_deny_authz(sbox): "verify if specifically denied paths dont sync" sbox.build() dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) run_init(dest_sbox.repo_url, sbox.repo_url) svntest.main.run_svn(None, "cp", os.path.join(sbox.wc_dir, "A"), os.path.join(sbox.wc_dir, "A_COPY") ) svntest.main.run_svn(None, "ci", "-mm", sbox.wc_dir) write_restrictive_svnserve_conf(sbox.repo_dir) # For mod_dav_svn's parent path setup we need per-repos permissions in # the authz file... if svntest.main.is_ra_type_dav(): src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules = { src_authz + ':/': '* = r', src_authz + ':/A': '* =', src_authz + ':/A_COPY/B/lambda': '* =', dst_authz + ':/': '* = rw', }) # Otherwise we can just go with the permissions needed for the source # repository. else: write_authz_file(sbox, None, prefixed_rules = { '/': '* = r', '/A': '* =', '/A_COPY/B/lambda': '* =', }) run_sync(dest_sbox.repo_url) lambda_url = dest_sbox.repo_url + '/A_COPY/B/lambda' # this file should have been blocked by authz svntest.actions.run_and_verify_svn([], svntest.verify.AnyOutput, 'cat', lambda_url)
def authz_switch_to_directory(sbox): "switched to directory, no read access on parents" sbox.build(read_only = True) write_authz_file(sbox, {"/": "*=rw", "/A/B": "*=", "/A/B/E": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') F_path = os.path.join(wc_dir, 'A', 'B', 'F') G_path = os.path.join(wc_dir, 'A', 'D', 'G') # Switch /A/B/E to /A/B/F. svntest.main.run_svn(None, 'switch', sbox.repo_url + "/A/B/E", G_path)
def broken_authz_file(sbox): "broken authz files cause errors" sbox.build(create_wc=False) # No characters but 'r', 'w', and whitespace are allowed as a value # in an authz rule. write_authz_file(sbox, {"/": "jrandom = rw # End-line comments disallowed"}) write_restrictive_svnserve_conf(sbox.repo_dir) out, err = svntest.main.run_svn(1, "delete", sbox.repo_url + "/A", "-m", "a log message") if out: raise svntest.verify.SVNUnexpectedStdout(out) if not err: raise svntest.verify.SVNUnexpectedStderr("Missing stderr")
def authz_locking(sbox): "test authz for locking" sbox.build() write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" root_url = sbox.repo_url wc_dir = sbox.wc_dir iota_url = root_url + '/iota' iota_path = os.path.join(wc_dir, 'iota') A_url = root_url + '/A' mu_path = os.path.join(wc_dir, 'A', 'mu') # lock a file url, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'lock', '-m', 'lock msg', iota_url) # lock a file path, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'lock', '-m', 'lock msg', iota_path) # Test for issue 2700: we have write access in folder /A, but not in root. # Get a lock on /A/mu and try to commit it. # lock a file path, target is writeable: should succeed svntest.actions.run_and_verify_svn(None, None, [], 'lock', '-m', 'lock msg', mu_path) svntest.main.file_append(mu_path, "hi") expected_output = svntest.wc.State(wc_dir, { 'A/mu': Item(verb='Sending'), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, [], None, mu_path)
def broken_authz_file(sbox): "broken authz files cause errors" sbox.build(create_wc = False) # No characters but 'r', 'w', and whitespace are allowed as a value # in an authz rule. write_authz_file(sbox, {"/": "jrandom = rw # End-line comments disallowed"}) write_restrictive_svnserve_conf(sbox.repo_dir) out, err = svntest.main.run_svn(1, "delete", sbox.repo_url + "/A", "-m", "a log message"); if out: raise svntest.verify.SVNUnexpectedStdout(out) if not err: raise svntest.verify.SVNUnexpectedStderr("Missing stderr")
def authz_switch_to_directory(sbox): "switched to directory, no read access on parents" sbox.build(read_only=True) write_authz_file(sbox, { "/": "*=rw", "/A/B": "*=", "/A/B/E": "jrandom = rw" }) write_restrictive_svnserve_conf(sbox.repo_dir) wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') F_path = os.path.join(wc_dir, 'A', 'B', 'F') G_path = os.path.join(wc_dir, 'A', 'D', 'G') # Switch /A/B/E to /A/B/F. svntest.main.run_svn(None, 'switch', sbox.repo_url + "/A/B/E", G_path)
def authz_validate(sbox): "test the authz validation rules" sbox.build(create_wc=False, read_only=True) write_restrictive_svnserve_conf(sbox.repo_dir) A_url = sbox.repo_url + '/A' # If any of the validate rules fail, the authz isn't loaded so there's no # access at all to the repository. # Test 1: Undefined group write_authz_file(sbox, {"/": "* = r", "/A/B": "@undefined_group = rw"}) if sbox.repo_url.startswith("http"): expected_err = ".*[Ff]orbidden.*" elif sbox.repo_url.startswith("svn"): expected_err = ".*Invalid authz configuration" else: expected_err = ".*@undefined_group.*" # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", None, expected_err, 'ls', A_url) # Test 2: Circular dependency write_authz_file(sbox, {"/": "* = r"}, { "groups": """admins = admin1, admin2, @devs devs1 = @admins, dev1 devs2 = @admins, dev2 devs = @devs1, dev3, dev4""" }) if sbox.repo_url.startswith("http"): expected_err = ".*[Ff]orbidden.*" elif sbox.repo_url.startswith("svn"): expected_err = ".*Invalid authz configuration" else: expected_err = ".*Circular dependency.*" # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", None, expected_err, 'ls', A_url) # Test 3: Group including other group 2 times (issue 2684) write_authz_file(sbox, {"/": "* = r"}, { "groups": """admins = admin1, admin2 devs1 = @admins, dev1 devs2 = @admins, dev2 users = @devs1, @devs2, user1, user2""" }) # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", ['B/\n', 'C/\n', 'D/\n', 'mu\n'], [], 'ls', A_url)
def authz_validate(sbox): "test the authz validation rules" sbox.build(create_wc = False, read_only = True) write_restrictive_svnserve_conf(sbox.repo_dir) A_url = sbox.repo_url + '/A' # If any of the validate rules fail, the authz isn't loaded so there's no # access at all to the repository. # Test 1: Undefined group write_authz_file(sbox, { "/" : "* = r", "/A/B" : "@undefined_group = rw" }) if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*@undefined_group.*" # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", None, expected_err, 'ls', A_url) # Test 2: Circular dependency write_authz_file(sbox, { "/" : "* = r" }, { "groups" : """admins = admin1, admin2, @devs devs1 = @admins, dev1 devs2 = @admins, dev2 devs = @devs1, dev3, dev4""" }) if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*Circular dependency.*" # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", None, expected_err, 'ls', A_url) # Test 3: Group including other group 2 times (issue 2684) write_authz_file(sbox, { "/" : "* = r" }, { "groups" : """admins = admin1, admin2 devs1 = @admins, dev1 devs2 = @admins, dev2 users = @devs1, @devs2, user1, user2""" }) # validation of this authz file should fail, so no repo access svntest.actions.run_and_verify_svn("ls remote folder", ['B/\n', 'C/\n', 'D/\n', 'mu\n'], [], 'ls', A_url)
def authz_checkout_test(sbox): "test authz for checkout" sbox.build(create_wc = False, read_only = True) local_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # 1st part: disable all read access, checkout should fail # write an authz file with *= on / if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, { "/": "* ="}) # checkout a working copy, should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'co', sbox.repo_url, local_dir) # 2nd part: now enable read access write_authz_file(sbox, { "/": "* = r"}) # checkout a working copy, should succeed because we have read access expected_output = svntest.main.greek_state.copy() expected_output.wc_dir = local_dir expected_output.tweak(status='A ', contents=None) expected_wc = svntest.main.greek_state svntest.actions.run_and_verify_checkout(sbox.repo_url, local_dir, expected_output, expected_wc)
def authz_access_required_at_repo_root2(sbox): "more authz issue #3242 - update to renamed file" sbox.build(create_wc = False) root_url = sbox.repo_url # Now we get all restrictive. write_authz_file(sbox, {'/': '* =', '/A': 'jrandom = rw'}) write_restrictive_svnserve_conf(sbox.repo_dir) # Rename a file. svntest.main.run_svn(None, 'mv', '-m', 'rename file in readable writable space', root_url + '/A/B/E/alpha', root_url + '/A/B/E/alpha-renamed') # Check out original greek sub tree below /A/B/E # and update it to the above rename. wc_dir = sbox.add_wc_path('ABE') os.mkdir(wc_dir) svntest.main.run_svn(None, 'co', '-r', '1', root_url + '/A/B/E', wc_dir) svntest.main.run_svn(None, 'up', wc_dir) # Rename a directory. svntest.main.run_svn(None, 'mv', '-m', 'rename diretory in readable writable space', root_url + '/A/D/H', root_url + '/A/D/a g e') # Check out original greek sub tree below /A/D # and update it to the above rename. wc_dir = sbox.add_wc_path('AD') os.mkdir(wc_dir) svntest.main.run_svn(None, 'co', '-r', '1', root_url + '/A/D', wc_dir) svntest.main.run_svn(None, 'up', wc_dir)
def authz_open_root(sbox): "authz issue #2486 - open root" sbox.build() write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"}) write_restrictive_svnserve_conf(sbox.repo_dir) # we have write access in folder /A, but not in root. Test on too # restrictive access needed in open_root by modifying a file in /A wc_dir = sbox.wc_dir mu_path = os.path.join(wc_dir, 'A', 'mu') svntest.main.file_append(mu_path, "hi") # Create expected output tree. expected_output = svntest.wc.State(wc_dir, { 'A/mu': Item(verb='Sending'), }) # Commit the one file. svntest.actions.run_and_verify_commit(wc_dir, expected_output, None, None, mu_path)
def copy_with_mod_from_unreadable_dir(sbox): "verify copies with mods from unreadable dirs" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/B', sbox.wc_dir + '/A/P') # Set a property inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'pset', 'foo', 'bar', sbox.wc_dir + '/A/P/lambda') # Add a new directory and file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'mkdir', sbox.wc_dir + '/A/P/NEW-DIR') svntest.main.file_append(sbox.wc_dir + '/A/P/E/new-file', "bla bla") svntest.main.run_svn(None, 'add', sbox.wc_dir + '/A/P/E/new-file') # Delete a file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'rm', sbox.wc_dir + '/A/P/E/beta') # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir, '-m', 'log_msg') # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules = { src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/new-file\n', ' A /A/P/F\n', ' A /A/P/NEW-DIR\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '2', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:12]) svntest.actions.run_and_verify_svn(['bar\n'], [], 'pget', 'foo', dest_sbox.repo_url + '/A/P/lambda')
def copy_with_mod_from_unreadable_dir(sbox): "verify copies with mods from unreadable dirs" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], "cp", sbox.wc_dir + "/A/B", sbox.wc_dir + "/A/P") # Set a property inside the copied directory. svntest.actions.run_and_verify_svn(None, [], "pset", "foo", "bar", sbox.wc_dir + "/A/P/lambda") # Add a new directory and file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], "mkdir", sbox.wc_dir + "/A/P/NEW-DIR") svntest.main.file_append(sbox.wc_dir + "/A/P/E/new-file", "bla bla") svntest.main.run_svn(None, "add", sbox.wc_dir + "/A/P/E/new-file") # Delete a file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], "rm", sbox.wc_dir + "/A/P/E/beta") # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], "ci", sbox.wc_dir, "-m", "log_msg") # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file( sbox, None, prefixed_rules={src_authz + ":/": "* = r", src_authz + ":/A/B": "* =", dst_authz + ":/": "* = rw"} ) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ "Changed paths:\n", " A /A/P\n", " A /A/P/E\n", " A /A/P/E/alpha\n", " A /A/P/E/new-file\n", " A /A/P/F\n", " A /A/P/NEW-DIR\n", " A /A/P/lambda\n", "\n", "\n", # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, "log", "-r", "2", "-v", dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, "LOG", expected_out, out[2:12]) svntest.actions.run_and_verify_svn(["bar\n"], [], "pget", "foo", dest_sbox.repo_url + "/A/P/lambda")
def copy_with_mod_from_unreadable_dir_and_copy(sbox): "verify copies with mods from unreadable dirs +copy" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/B', sbox.wc_dir + '/A/P') # Copy a (readable) file into the copied directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/D/gamma', sbox.wc_dir + '/A/P/E') # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir, '-m', 'log_msg') # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={ src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/beta\n', ' A /A/P/E/gamma (from /A/D/gamma:1)\n', ' A /A/P/F\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '2', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:12])
def mergeinfo_and_skipped_paths(sbox): "skipped paths get overriding mergeinfo" # Test that we override the mergeinfo for child paths which weren't # actually merged because they were skipped. # # This test covers paths skipped because: # # 1) The source of a merge is inaccessible due to authz restrictions. # 2) Destination of merge is inaccessible due to authz restrictions. # 3) Source *and* destination of merge is inaccessible due to authz # restrictions. sbox.build() wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox, False, 3) # Create a restrictive authz where part of the merge source and part # of the target are inaccesible. write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw", # Make a directory in the merge source inaccessible. "/A/B/E" : svntest.main.wc_author + "=", # Make a file and dir in the merge destination # inaccessible. "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=", "/A_COPY_2/D/G" : svntest.main.wc_author + "=", # Make the source and destination inaccessible. "/A_COPY_3/B/E" : svntest.main.wc_author + "=", }) # Checkout just the branch under the newly restricted authz. wc_restricted = sbox.add_wc_path('restricted') svntest.actions.run_and_verify_svn(None, None, [], 'checkout', sbox.repo_url, wc_restricted) # Some paths we'll use in the second WC. A_COPY_path = os.path.join(wc_restricted, "A_COPY") A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2") A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H") A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3") omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega") zeta_path = sbox.ospath("A/D/H/zeta") # Merge r4:8 into the restricted WC's A_COPY. # # We expect A_COPY/B/E to be skipped because we can't access the source # and A_COPY/D/H/omega because it is missing. Since we have A_COPY/B/E # we should override it's inherited mergeinfo, giving it just what it # inherited from A_COPY before the merge. expected_output = wc.State(A_COPY_path, { 'D/G/rho' : Item(status='U '), 'D/H/psi' : Item(status='U '), 'D/H/omega' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' U'), 'B/E' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_path, { }) expected_status = wc.State(A_COPY_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status='M ', wc_rev=8), 'D/H/omega' : Item(status='M ', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/psi' : Item("New content"), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/omega' : Item("New content"), 'D/H' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/G' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_path, { 'B/E' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1) # Merge r4:8 into the restricted WC's A_COPY_2. # # As before we expect A_COPY_2/B/E to be skipped because we can't access the # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have # access. # # After the merge the parents of the missing dest paths, A_COPY_2/D and # A_COPY_2/D/H get non-inheritable mergeinfo. Those parents' children that # *are* present and are affected by the merge, only A_COPY_2/D/H/omega in # this case, get their own mergeinfo. Note that A_COPY_2/D/H is both the # parent of a missing child and the sibling of missing child, but the former # always takes precedence in terms of getting *non*-inheritable mergeinfo. expected_output = wc.State(A_COPY_2_path, { 'D/H/omega' : Item(status='U '), # Below the skip 'D/G/rho' : Item(status=' ', treeconflict='U'), }) expected_mergeinfo_output = wc.State(A_COPY_2_path, { '' : Item(status=' U'), 'D' : Item(status=' U'), 'D/H' : Item(status=' U'), 'D/H/omega' : Item(status=' U'), 'B/E' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_path, { }) expected_status = wc.State(A_COPY_2_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/omega' : Item(status='MM', wc_rev=8), 'D/H' : Item(status=' M', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' M', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_2_path, { 'B/E' : Item(verb='Skipped missing target'), 'D/G' : Item(verb='Skipped missing target'), 'D/H/psi' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_2_path, '4', '8', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) # Merge r5:7 into the restricted WC's A_COPY_3. # # Again A_COPY_3/B/E should be skipped, but because we can't access the # source *or* the destination we expect its parent A_COPY_3/B to get # non-inheritable mergeinfo. A_COPY_3B's two existing siblings, # A_COPY_3/B/F and A_COPY_3/B/lambda are untouched by the merge so # neither gets any mergeinfo recorded. expected_output = wc.State(A_COPY_3_path, { 'D/G/rho' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_COPY_3_path, { '' : Item(status=' U'), 'B' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_3_path, { }) expected_status = wc.State(A_COPY_3_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/omega' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status=' ', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' M', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}), 'D/H/omega' : Item("This is the file 'omega'.\n"), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/psi' : Item("This is the file 'psi'.\n"), 'D/H' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/F' : Item(), 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_3_path, {'B/E' : Item(verb='Skipped missing target')}) svntest.actions.run_and_verify_merge(A_COPY_3_path, '5', '7', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_restricted) # Test issue #2997. If a merge requires two separate editor drives and the # first is non-operative we should still update the mergeinfo to reflect # this. # # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H. r5 gets merged first # but is a no-op, r8 get's merged next and is operative so the mergeinfo # should be updated on the merge target to reflect both merges. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), }) expected_elision_output = wc.State(A_COPY_2_H_path, { }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), 'chi' : Item("This is the file 'chi'.\n"), }) expected_skip = wc.State(A_COPY_2_H_path, { 'psi' : Item(verb='Skipped missing target'), }) # Note we don't bother checking expected mergeinfo output because the # multiple merges being performed here, -c5 and -c8, will result in # first ' U' and then ' G' mergeinfo notifications. Our expected # tree structures can't handle checking for multiple values for the # same key. svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '5', sbox.repo_url + '/A/D/H', None, expected_output, None, # expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0, '-c5', '-c8', A_COPY_2_H_path) # Test issue #2829 'Improve handling for skipped paths encountered # during a merge' # Revert previous changes to restricted WC svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_restricted) # Add new path 'A/D/H/zeta' svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n") svntest.actions.run_and_verify_svn(None, None, [], 'add', zeta_path) expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')}) wc_status.add({'A/D/H/zeta' : Item(status=' ', wc_rev=9)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) # Merge -r7:9 to the restricted WC's A_COPY_2/D/H. # # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi', # but since 'psi' is untouched by the merge it isn't skipped, and since it # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable # mergeinfo set on it to describe the merge, so none of the parent's # children will get explicit mergeinfo -- see issue #4056. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), 'zeta' : Item(status='A '), }) expected_mergeinfo_output = wc.State(A_COPY_2_H_path, { '' : Item(status=' U'), 'omega' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status=' U'), }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='M ', wc_rev=8), 'zeta' : Item(status='A ', copied='+', wc_rev='-'), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}), 'omega' : Item("New content"), 'chi' : Item("This is the file 'chi'.\n"), 'zeta' : Item("This is the file 'zeta'.\n"), }) expected_skip = wc.State(A_COPY_2_H_path, {}) svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9', sbox.repo_url + '/A/D/H', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) # Merge -r4:9 to the restricted WC's A_COPY_2/D/H. # # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit # mergeinfo from this merge. svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_restricted) expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), 'zeta' : Item(status='A '), }) expected_mergeinfo_output = wc.State(A_COPY_2_H_path, { '' : Item(status=' U'), 'omega' : Item(status=' U'), 'zeta' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_H_path, { }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), 'zeta' : Item(status='A ', copied='+', wc_rev='-'), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}), 'chi' : Item("This is the file 'chi'.\n"), 'zeta' : Item("This is the file 'zeta'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}), }) expected_skip = wc.State(A_COPY_2_H_path, { 'psi' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9', sbox.repo_url + '/A/D/H', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0)
def merge_fails_if_subtree_is_deleted_on_src(sbox): "merge fails if subtree is deleted on src" ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ## # Create a WC sbox.build() wc_dir = sbox.wc_dir if is_ra_type_svn() or is_ra_type_dav(): write_authz_file(sbox, {"/" : "* = rw", "/unrelated" : ("* =\n" + svntest.main.wc_author2 + " = rw")}) # Some paths we'll care about Acopy_path = sbox.ospath('A_copy') gamma_path = sbox.ospath('A/D/gamma') Acopy_gamma_path = sbox.ospath('A_copy/D/gamma') Acopy_D_path = sbox.ospath('A_copy/D') A_url = sbox.repo_url + '/A' Acopy_url = sbox.repo_url + '/A_copy' # Contents to be added to 'gamma' new_content = "line1\nline2\nline3\nline4\nline5\n" svntest.main.file_write(gamma_path, new_content) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.tweak('A/D/gamma', wc_rev=2) # Commit the new content svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url, '-m', 'create a new copy of A') # Update working copy svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) svntest.main.file_substitute(gamma_path, "line1", "this is line1") # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status.tweak(wc_rev=3) expected_status.tweak('A/D/gamma', wc_rev=4) expected_status.add({ 'A_copy' : Item(status=' ', wc_rev=3), 'A_copy/B' : Item(status=' ', wc_rev=3), 'A_copy/B/lambda' : Item(status=' ', wc_rev=3), 'A_copy/B/E' : Item(status=' ', wc_rev=3), 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3), 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3), 'A_copy/B/F' : Item(status=' ', wc_rev=3), 'A_copy/mu' : Item(status=' ', wc_rev=3), 'A_copy/C' : Item(status=' ', wc_rev=3), 'A_copy/D' : Item(status=' ', wc_rev=3), 'A_copy/D/gamma' : Item(status=' ', wc_rev=3), 'A_copy/D/G' : Item(status=' ', wc_rev=3), 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3), 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3), 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3), 'A_copy/D/H' : Item(status=' ', wc_rev=3), 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3), 'A_copy/D/H/omega': Item(status=' ', wc_rev=3), 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Delete A/D/gamma from working copy svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Deleting'), }) expected_status.remove('A/D/gamma') svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir, wc_dir) svntest.actions.run_and_verify_svn( None, expected_merge_output([[3,4]], ['U ' + Acopy_gamma_path + '\n', ' U ' + Acopy_gamma_path + '\n']), [], 'merge', '-r1:4', A_url + '/D/gamma' + '@4', Acopy_gamma_path) # r6: create an empty (unreadable) commit. # Empty or unreadable revisions used to crash a svn 1.6+ client when # used with a 1.5 server: # http://svn.haxx.se/dev/archive-2009-04/0476.shtml svntest.main.run_svn(None, 'mkdir', sbox.repo_url + '/unrelated', '--username', svntest.main.wc_author2, '-m', 'creating a rev with no paths.') # A delete merged ontop of a modified file is normally a tree conflict, # see notes/tree-conflicts/detection.txt, but --force currently avoids # this. svntest.actions.run_and_verify_svn( None, expected_merge_output([[3,6]], ['D ' + Acopy_gamma_path + '\n', ' U ' + Acopy_path + '\n']), [], 'merge', '-r1:6', '--force', A_url, Acopy_path)
def authz_log_and_tracing_test(sbox): "test authz for log and tracing path changes" sbox.build() wc_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # write an authz file with *=rw on / if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, {"/": "* = rw\n"}) root_url = sbox.repo_url D_url = root_url + '/A/D' G_url = D_url + '/G' # check if log doesn't spill any info on which you don't have read access rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') svntest.main.file_append(rho_path, 'new appended text for rho') svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'add file rho', sbox.wc_dir) svntest.main.file_append(rho_path, 'extra change in rho') svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'changed file rho', sbox.wc_dir) # copy a remote file svntest.actions.run_and_verify_svn(None, None, [], 'cp', rho_path, D_url, '-m', 'copy rho to readable area') # now disable read access on the first version of rho, keep the copy in # /A/D readable. if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" authz = {"/": "* = rw", "/A/D/G": "* ="} write_authz_file(sbox, authz) ## log # changed file in this rev. is not readable anymore, so author and date # should be hidden, like this: # r2 | (no author) | (no date) | 1 line svntest.actions.run_and_verify_svn(None, ".*(no author).*(no date).*|-+\n|\n", [], 'log', '-r', '2', '--limit', '1', wc_dir) if sbox.repo_url.startswith('http'): expected_err2 = expected_err else: expected_err2 = ".*svn: Item is not readable.*" # if we do the same thing directly on the unreadable file, we get: # svn: Item is not readable svntest.actions.run_and_verify_svn(None, None, expected_err2, 'log', rho_path) # while the HEAD rev of the copy is readable in /A/D, its parent in # /A/D/G is not, so don't spill any info there either. svntest.actions.run_and_verify_svn(None, ".*(no author).*(no date).*|-+\n|\n", [], 'log', '-r', '2', '--limit', '1', D_url) # Test that only author/date are shown for partially visible revisions. svntest.actions.enable_revprop_changes(sbox.repo_dir) write_authz_file(sbox, {"/": "* = rw"}) svntest.actions.run_and_verify_svn( None, None, [], # message, expected_stdout, expected_stderr 'ps', '--revprop', '-r1', 'foobar', 'foo bar', sbox.repo_url) svntest.actions.run_and_verify_log_xml( expected_revprops=[{ 'svn:author': svntest.main.wc_author, 'svn:date': '', 'svn:log': 'Log message for revision 1.', 'foobar': 'foo bar' }], args=['--with-all-revprops', '-r1', sbox.repo_url]) write_authz_file(sbox, authz) svntest.actions.run_and_verify_log_xml( expected_revprops=[{ 'svn:author': svntest.main.wc_author, 'svn:date': '' }], args=['--with-all-revprops', '-r1', sbox.repo_url]) ## cat # now see if we can look at the older version of rho svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', '-r', '2', D_url + '/rho') if sbox.repo_url.startswith('http'): expected_err2 = expected_err else: expected_err2 = ".*svn: Unreadable path encountered; access denied.*" svntest.actions.run_and_verify_svn(None, None, expected_err2, 'cat', '-r', '2', G_url + '/rho') ## diff # we shouldn't see the diff of a file in an unreadable path svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', 'HEAD', G_url + '/rho') svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', '2', D_url + '/rho') svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', '2:4', D_url + '/rho')
def diff_unauth_parent(sbox): "diff directory without reading parent" sbox.build(create_wc=False) # Create r2: Change A a bit svntest.actions.run_and_verify_svnmucc(None, [], 'propset', 'k', 'v', sbox.repo_url + '/A', '-m', 'set prop') # Create r3 Mark E and G svntest.actions.run_and_verify_svnmucc(None, [], 'propset', 'this-is', 'E', sbox.repo_url + '/A/B/E', 'propset', 'this-is', 'G', sbox.repo_url + '/A/D/G', '-m', 'set prop') # Create r4: Replace A/B/E with A/D/G svntest.actions.run_and_verify_svnmucc(None, [], 'rm', sbox.repo_url + '/A/B/E', 'cp', '3', sbox.repo_url + '/A/D/G', sbox.repo_url + '/A/B/E', '-m', 'replace A/B/E') if is_ra_type_svn() or is_ra_type_dav(): write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : "* =", "/A" : "* = rw"}) # Diff the property change expected_output = [ 'Index: .\n', '===================================================================\n', '--- .\t(revision 1)\n', '+++ .\t(revision 2)\n', '\n', 'Property changes on: .\n', '___________________________________________________________________\n', 'Added: k\n', '## -0,0 +1 ##\n', '+v\n', '\ No newline at end of property\n' ] svntest.actions.run_and_verify_svn(expected_output, [], 'diff', sbox.repo_url + '/A', '-c', '2') if is_ra_type_svn() or is_ra_type_dav(): write_authz_file(sbox, {"/" : "* =", "/A/B/E" : "* = rw"}) # Diff the replacement expected_output = [ 'Index: alpha\n', '===================================================================\n', '--- alpha\t(revision 3)\n', '+++ alpha\t(nonexistent)\n', '@@ -1 +0,0 @@\n', '-This is the file \'alpha\'.\n', 'Index: beta\n', '===================================================================\n', '--- beta\t(revision 3)\n', '+++ beta\t(nonexistent)\n', '@@ -1 +0,0 @@\n', '-This is the file \'beta\'.\n', 'Index: tau\n', '===================================================================\n', '--- tau\t(nonexistent)\n', '+++ tau\t(revision 4)\n', '@@ -0,0 +1 @@\n', '+This is the file \'tau\'.\n', 'Index: rho\n', '===================================================================\n', '--- rho\t(nonexistent)\n', '+++ rho\t(revision 4)\n', '@@ -0,0 +1 @@\n', '+This is the file \'rho\'.\n', 'Index: pi\n', '===================================================================\n', '--- pi\t(nonexistent)\n', '+++ pi\t(revision 4)\n', '@@ -0,0 +1 @@\n', '+This is the file \'pi\'.\n', ] if is_ra_type_svn() or is_ra_type_dav(): # Because we can't anchor above C we see just a changed C, not a # replacement expected_output += [ 'Index: .\n', '===================================================================\n', '--- .\t(revision 3)\n', '+++ .\t(revision 4)\n', '\n', 'Property changes on: .\n', '___________________________________________________________________\n', 'Modified: this-is\n', '## -1 +1 ##\n', '-E\n', '\ No newline at end of property\n', '+G\n', '\ No newline at end of property\n', ] else: # ### We should also see a property deletion here! expected_output += [ 'Index: .\n', '===================================================================\n', '--- .\t(revision 3)\n', '+++ .\t(nonexistent)\n', '\n', 'Property changes on: .\n', '___________________________________________________________________\n', 'Deleted: this-is\n', '## -1 +0,0 ##\n', '-E\n', '\ No newline at end of property\n', 'Index: .\n', '===================================================================\n', '--- .\t(nonexistent)\n', '+++ .\t(revision 4)\n', '\n', 'Property changes on: .\n', '___________________________________________________________________\n', 'Added: this-is\n', '## -0,0 +1 ##\n', '+G\n', '\ No newline at end of property\n', ] # Use two url diff, because 'svn diff url -c' uses copyfrom to diff against expected_output = svntest.verify.UnorderedOutput(expected_output) svntest.actions.run_and_verify_svn(expected_output, [], 'diff', sbox.repo_url + '/A/B/E@3', sbox.repo_url + '/A/B/E@4', '--notice-ancestry') # Do the same thing with summarize to really see directory deletes and adds if is_ra_type_svn() or is_ra_type_dav(): # With no rights on the parent directory we just see a property change on E expected_output = [ 'D %s/A/B/E/alpha\n' % sbox.repo_url, 'D %s/A/B/E/beta\n' % sbox.repo_url, 'A %s/A/B/E/tau\n' % sbox.repo_url, 'A %s/A/B/E/rho\n' % sbox.repo_url, 'A %s/A/B/E/pi\n' % sbox.repo_url, ' M %s/A/B/E\n' % sbox.repo_url, ] else: # But with rights on the parent we see a replacement of E expected_output = [ 'D %s/A/B/E/alpha\n' % sbox.repo_url, 'D %s/A/B/E/beta\n' % sbox.repo_url, 'D %s/A/B/E\n' % sbox.repo_url, 'A %s/A/B/E/tau\n' % sbox.repo_url, 'A %s/A/B/E/rho\n' % sbox.repo_url, 'A %s/A/B/E/pi\n' % sbox.repo_url, 'A %s/A/B/E\n' % sbox.repo_url, ] expected_output = svntest.verify.UnorderedOutput(expected_output) svntest.actions.run_and_verify_svn(expected_output, [], 'diff', sbox.repo_url + '/A/B/E@3', sbox.repo_url + '/A/B/E@4', '--notice-ancestry', '--summarize')
def iprops_authz(sbox): "property inheritance and read restricted parents" sbox.build() wc_dir = sbox.wc_dir # r2 - Set properties at various levels. sbox.simple_propset('RootProp', 'Root-Prop-Val', '.') sbox.simple_propset('BranchProp', 'Branch-Prop-Val', 'A') sbox.simple_propset('RandomProp1', 'Random-Prop-Val-1', 'A/D') sbox.simple_propset('RandomProp2', 'Random-Prop-Val-2', 'A/D/H') sbox.simple_propset('FileProp1', 'File-Prop-Val-1', 'A/D/H/psi') svntest.main.run_svn(None, 'commit', '-m', 'Add some properties', wc_dir) write_restrictive_svnserve_conf(sbox.repo_dir) # Check that a restricted user can only see inherited props from # parent paths which he has read access to. # Grant access only to ^/A/D/H/psi. No inherited properties should # be shown. write_authz_file(sbox, { "/A/D/H/psi": svntest.main.wc_author + "=rw", }) expected_iprops = {} expected_explicit_props = {'FileProp1': 'File-Prop-Val-1'} svntest.actions.run_and_verify_inherited_prop_xml( sbox.repo_url + '/A/D/H/psi', expected_iprops, expected_explicit_props) # Grant access to ^/A/D/H/psi and the repos root but not the intermediate # paths between the two. write_authz_file( sbox, { "/": svntest.main.wc_author + "=rw", "/A": svntest.main.wc_author + "=", "/A/D/H/psi": svntest.main.wc_author + "=rw", }) expected_iprops = {sbox.repo_url: {'RootProp': 'Root-Prop-Val'}} svntest.actions.run_and_verify_inherited_prop_xml( sbox.repo_url + '/A/D/H/psi', expected_iprops, expected_explicit_props) # Grant access to ^/A/D/H/psi, the repos root, and the intermediate path # ^/A/D. Everything else is still blocked. write_authz_file( sbox, { "/": svntest.main.wc_author + "=rw", "/A": svntest.main.wc_author + "=", "/A/D": svntest.main.wc_author + "=rw", "/A/D/H": svntest.main.wc_author + "=", "/A/D/H/psi": svntest.main.wc_author + "=rw", }) expected_iprops = { sbox.repo_url: { 'RootProp': 'Root-Prop-Val' }, sbox.repo_url + '/A/D': { 'RandomProp1': 'Random-Prop-Val-1' } } svntest.actions.run_and_verify_inherited_prop_xml( sbox.repo_url + '/A/D/H/psi', expected_iprops, expected_explicit_props) # Grant read access to everything except ^/A/D/H/psi. In this case we # should get an authorization failed error. It doesn't matter that we can # read the parents. write_authz_file( sbox, { "/": svntest.main.wc_author + "=rw", "/A/D/H/psi": svntest.main.wc_author + "=", }) if svntest.main.is_ra_type_dav(): expected_err = ".*[Ff]orbidden.*" else: expected_err = ".*svn: E170001: Authorization failed.*" svntest.actions.run_and_verify_svn(None, expected_err, 'proplist', '-v', '--show-inherited-props', sbox.repo_url + '/A/D/H/psi')
def authz_read_access(sbox): "test authz for read operations" sbox.build(create_wc = False) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' C_url = A_url + '/C' E_url = B_url + '/E' mu_url = A_url + '/mu' iota_url = root_url + '/iota' lambda_url = B_url + '/lambda' alpha_url = E_url + '/alpha' D_url = A_url + '/D' G_url = D_url + '/G' pi_url = G_url + '/pi' H_url = D_url + '/H' chi_url = H_url + '/chi' if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" # create some folders with spaces in their names svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'logmsg', B_url+'/folder with spaces', B_url+'/folder with spaces/empty folder') write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, { "/": "* = r", "/A/B": "* =", "/A/D": "* = rw", "/A/D/G": ("* = rw\n" + svntest.main.wc_author + " ="), "/A/D/H": ("* = \n" + svntest.main.wc_author + " = rw"), "/A/B/folder with spaces": (svntest.main.wc_author + " = r")}) # read a remote file svntest.actions.run_and_verify_svn(None, ["This is the file 'iota'.\n"], [], 'cat', iota_url) # read a remote file, readably by user specific exception svntest.actions.run_and_verify_svn(None, ["This is the file 'chi'.\n"], [], 'cat', chi_url) # read a remote file, unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', lambda_url) # read a remote file, unreadable through recursion: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', alpha_url) # read a remote file, user specific authorization is ignored because * = rw svntest.actions.run_and_verify_svn(None, ["This is the file 'pi'.\n"], [], 'cat', pi_url) # open a remote folder(ls) svntest.actions.run_and_verify_svn("ls remote root folder", ["A/\n", "iota\n"], [], 'ls', root_url) # open a remote folder(ls), unreadable: should fail svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput, 'ls', B_url) # open a remote folder(ls) with spaces, should succeed svntest.actions.run_and_verify_svn(None, None, [], 'ls', B_url+'/folder with spaces/empty folder') # open a remote folder(ls), unreadable through recursion: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'ls', E_url) # copy a remote file svntest.actions.run_and_verify_svn(None, None, [], 'cp', iota_url, D_url, '-m', 'logmsg') # copy a remote file, source is unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', lambda_url, D_url) # copy a remote folder svntest.actions.run_and_verify_svn(None, None, [], 'cp', C_url, D_url, '-m', 'logmsg') # copy a remote folder, source is unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', E_url, D_url)
def authz_svnserve_anon_access_read(sbox): "authz issue #2712" sbox.build(create_wc = False) svntest.main.safe_rmtree(sbox.wc_dir) B_path = os.path.join(sbox.wc_dir, 'A', 'B') other_B_path = B_path + '_other' B_url = sbox.repo_url + '/A/B' D_path = os.path.join(sbox.wc_dir, 'A', 'D') D_url = sbox.repo_url + '/A/D' # We want a svnserve.conf with anon-access = read. write_restrictive_svnserve_conf(sbox.repo_dir, "read") # Give jrandom read access to /A/B. Anonymous users can only # access /A/D. write_authz_file(sbox, { "/A/B" : "jrandom = rw", "/A/D" : "* = r" }) # Perform a checkout of /A/B, expecting to see no errors. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', B_url, B_path) # Anonymous users should be able to check out /A/D. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', D_url, D_path) # Now try a switch. svntest.main.safe_rmtree(D_path) svntest.actions.run_and_verify_svn(None, None, [], 'switch', D_url, B_path) # Check out /A/B with an unknown username, expect error. svntest.actions.run_and_verify_svn( None, None, ".*Authentication error from server: Username not found.*", 'checkout', '--non-interactive', '--username', 'losing_user', B_url, B_path + '_unsuccessful') # Check out a second copy of /A/B, make changes for later merge. svntest.actions.run_and_verify_svn(None, None, [], 'checkout', B_url, other_B_path) other_alpha_path = os.path.join(other_B_path, 'E', 'alpha') svntest.main.file_append(other_alpha_path, "fish\n") svntest.actions.run_and_verify_svn(None, None, [], 'commit', '-m', 'log msg', other_B_path) # Now try to merge. This is an atypical merge, since our "branch" # is not really a branch (it's the same URL), but we only care about # authz here, not the semantics of the merge. (Merges had been # failing in authz, for the reasons summarized in # http://subversion.tigris.org/issues/show_bug.cgi?id=2712#desc13.) svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c', '2', B_url, B_path)
def authz_write_access(sbox): "test authz for write operations" sbox.build(create_wc = False) write_restrictive_svnserve_conf(sbox.repo_dir) if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Access denied.*" write_authz_file(sbox, { "/": "* = r", "/A/B": "* = rw", "/A/C": "* = rw"}) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' C_url = A_url + '/C' E_url = B_url + '/E' mu_url = A_url + '/mu' iota_url = root_url + '/iota' lambda_url = B_url + '/lambda' D_url = A_url + '/D' # copy a remote file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', lambda_url, D_url) # copy a remote folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', E_url, D_url) # delete a file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'rm', '-m', 'logmsg', iota_url) # delete a folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'rm', '-m', 'logmsg', D_url) # create a folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mkdir', '-m', 'logmsg', A_url+'/newfolder') # move a remote file, source is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', mu_url, C_url) # move a remote folder, source is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', D_url, C_url) # move a remote file, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', lambda_url, D_url) # move a remote folder, target is readonly: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'mv', '-m', 'logmsg', B_url, D_url)
def copy_with_mod_from_unreadable_dir_and_copy(sbox): "verify copies with mods from unreadable dirs +copy" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/B', sbox.wc_dir + '/A/P') # Copy a (readable) file into the copied directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/D/gamma', sbox.wc_dir + '/A/P/E') # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir, '-m', 'log_msg') # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules = { src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/beta\n', ' A /A/P/E/gamma (from /A/D/gamma:1)\n', ' A /A/P/F\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '2', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:12])
def merge_fails_if_subtree_is_deleted_on_src(sbox): "merge fails if subtree is deleted on src" ## See https://issues.apache.org/jira/browse/SVN-2876. ## # Create a WC sbox.build() wc_dir = sbox.wc_dir if is_ra_type_svn() or is_ra_type_dav(): write_authz_file(sbox, {"/" : "* = rw", "/unrelated" : ("* =\n" + svntest.main.wc_author2 + " = rw")}) # Some paths we'll care about Acopy_path = sbox.ospath('A_copy') gamma_path = sbox.ospath('A/D/gamma') Acopy_gamma_path = sbox.ospath('A_copy/D/gamma') Acopy_D_path = sbox.ospath('A_copy/D') A_url = sbox.repo_url + '/A' Acopy_url = sbox.repo_url + '/A_copy' # Contents to be added to 'gamma' new_content = "line1\nline2\nline3\nline4\nline5\n" svntest.main.file_write(gamma_path, new_content) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.tweak('A/D/gamma', wc_rev=2) # Commit the new content svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status) svntest.actions.run_and_verify_svn(None, [], 'cp', A_url, Acopy_url, '-m', 'create a new copy of A') # Update working copy svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir) svntest.main.file_substitute(gamma_path, "line1", "this is line1") # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status.tweak(wc_rev=3) expected_status.tweak('A/D/gamma', wc_rev=4) expected_status.add({ 'A_copy' : Item(status=' ', wc_rev=3), 'A_copy/B' : Item(status=' ', wc_rev=3), 'A_copy/B/lambda' : Item(status=' ', wc_rev=3), 'A_copy/B/E' : Item(status=' ', wc_rev=3), 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3), 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3), 'A_copy/B/F' : Item(status=' ', wc_rev=3), 'A_copy/mu' : Item(status=' ', wc_rev=3), 'A_copy/C' : Item(status=' ', wc_rev=3), 'A_copy/D' : Item(status=' ', wc_rev=3), 'A_copy/D/gamma' : Item(status=' ', wc_rev=3), 'A_copy/D/G' : Item(status=' ', wc_rev=3), 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3), 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3), 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3), 'A_copy/D/H' : Item(status=' ', wc_rev=3), 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3), 'A_copy/D/H/omega': Item(status=' ', wc_rev=3), 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status) # Delete A/D/gamma from working copy svntest.actions.run_and_verify_svn(None, [], 'delete', gamma_path) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Deleting'), }) expected_status.remove('A/D/gamma') svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, [], wc_dir, wc_dir) svntest.actions.run_and_verify_svn( expected_merge_output([[3,4]], ['U ' + Acopy_gamma_path + '\n', ' U ' + Acopy_gamma_path + '\n']), [], 'merge', '-r1:4', A_url + '/D/gamma' + '@4', Acopy_gamma_path) # r6: create an empty (unreadable) commit. # Empty or unreadable revisions used to crash a svn 1.6+ client when # used with a 1.5 server: # http://svn.haxx.se/dev/archive-2009-04/0476.shtml svntest.main.run_svn(None, 'mkdir', sbox.repo_url + '/unrelated', '--username', svntest.main.wc_author2, '-m', 'creating a rev with no paths.') # A delete merged ontop of a modified file is normally a tree conflict, # see notes/tree-conflicts/detection.txt, but --force currently avoids # this. svntest.actions.run_and_verify_svn( expected_merge_output([[3,6]], ['D ' + Acopy_gamma_path + '\n', ' U ' + Acopy_path + '\n']), [], 'merge', '-r1:6', '--force', A_url, Acopy_path)
def copy_from_unreadable_dir(sbox): "verify that copies from unreadable dirs work" sbox.build() B_url = sbox.repo_url + '/A/B' P_url = sbox.repo_url + '/A/P' # Set a property on the directory we're going to copy, and a file in it, to # confirm that they're transmitted when we later sync the copied directory svntest.actions.run_and_verify_svn(None, [], 'pset', 'foo', 'bar', sbox.wc_dir + '/A/B/lambda') svntest.actions.run_and_verify_svn(None, [], 'pset', 'baz', 'zot', sbox.wc_dir + '/A/B') svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir + '/A/B', '-m', 'log_msg') # Now copy that directory so we'll see it in our synced copy svntest.actions.run_and_verify_svn(None, [], 'cp', B_url, P_url, '-m', 'Copy B to P') write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules = { src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/beta\n', ' A /A/P/F\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '3', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:11]) svntest.actions.run_and_verify_svn(['bar\n'], [], 'pget', 'foo', dest_sbox.repo_url + '/A/P/lambda') svntest.actions.run_and_verify_svn(['zot\n'], [], 'pget', 'baz', dest_sbox.repo_url + '/A/P')
def mergeinfo_and_skipped_paths(sbox): "skipped paths get overriding mergeinfo" # Test that we override the mergeinfo for child paths which weren't # actually merged because they were skipped. # # This test covers paths skipped because: # # 1) The source of a merge is inaccessible due to authz restrictions. # 2) Destination of merge is inaccessible due to authz restrictions. # 3) Source *and* destination of merge is inaccessible due to authz # restrictions. sbox.build() wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox, False, 3) # Create a restrictive authz where part of the merge source and part # of the target are inaccesible. write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw", # Make a directory in the merge source inaccessible. "/A/B/E" : svntest.main.wc_author + "=", # Make a file and dir in the merge destination # inaccessible. "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=", "/A_COPY_2/D/G" : svntest.main.wc_author + "=", # Make the source and destination inaccessible. "/A_COPY_3/B/E" : svntest.main.wc_author + "=", }) # Checkout just the branch under the newly restricted authz. wc_restricted = sbox.add_wc_path('restricted') svntest.actions.run_and_verify_svn(None, [], 'checkout', sbox.repo_url, wc_restricted) # Some paths we'll use in the second WC. A_COPY_path = os.path.join(wc_restricted, "A_COPY") A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2") A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H") A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3") omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega") zeta_path = sbox.ospath("A/D/H/zeta") # Merge r4:8 into the restricted WC's A_COPY. # # We expect A_COPY/B/E to be skipped because we can't access the source # and A_COPY/D/H/omega because it is missing. Since we have A_COPY/B/E # we should override it's inherited mergeinfo, giving it just what it # inherited from A_COPY before the merge. expected_output = wc.State(A_COPY_path, { 'D/G/rho' : Item(status='U '), 'D/H/psi' : Item(status='U '), 'D/H/omega' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_COPY_path, { '' : Item(status=' U'), 'B/E' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_path, { }) expected_status = wc.State(A_COPY_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status='M ', wc_rev=8), 'D/H/omega' : Item(status='M ', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/psi' : Item("New content"), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/omega' : Item("New content"), 'D/H' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/G' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_path, { 'B/E' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, check_props=True) # Merge r4:8 into the restricted WC's A_COPY_2. # # As before we expect A_COPY_2/B/E to be skipped because we can't access the # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have # access. # # After the merge the parents of the missing dest paths, A_COPY_2/D and # A_COPY_2/D/H get non-inheritable mergeinfo. Those parents' children that # *are* present and are affected by the merge, only A_COPY_2/D/H/omega in # this case, get their own mergeinfo. Note that A_COPY_2/D/H is both the # parent of a missing child and the sibling of missing child, but the former # always takes precedence in terms of getting *non*-inheritable mergeinfo. expected_output = wc.State(A_COPY_2_path, { 'D/H/omega' : Item(status='U '), # Below the skip 'D/G/rho' : Item(status=' ', treeconflict='U'), }) expected_mergeinfo_output = wc.State(A_COPY_2_path, { '' : Item(status=' U'), 'D' : Item(status=' U'), 'D/H' : Item(status=' U'), 'D/H/omega' : Item(status=' U'), 'B/E' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_path, { }) expected_status = wc.State(A_COPY_2_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/omega' : Item(status='MM', wc_rev=8), 'D/H' : Item(status=' M', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' M', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_2_path, { 'B/E' : Item(verb='Skipped missing target'), 'D/G' : Item(verb='Skipped missing target'), 'D/H/psi' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_2_path, '4', '8', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, check_props=True) # Merge r5:7 into the restricted WC's A_COPY_3. # # Again A_COPY_3/B/E should be skipped, but because we can't access the # source *or* the destination we expect its parent A_COPY_3/B to get # non-inheritable mergeinfo. A_COPY_3B's two existing siblings, # A_COPY_3/B/F and A_COPY_3/B/lambda are untouched by the merge so # neither gets any mergeinfo recorded. expected_output = wc.State(A_COPY_3_path, { 'D/G/rho' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_COPY_3_path, { '' : Item(status=' U'), 'B' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_3_path, { }) expected_status = wc.State(A_COPY_3_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/omega' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status=' ', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' M', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}), 'D/H/omega' : Item("This is the file 'omega'.\n"), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/psi' : Item("This is the file 'psi'.\n"), 'D/H' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/F' : Item(), 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_3_path, {'B/E' : Item(verb='Skipped missing target')}) svntest.actions.run_and_verify_merge(A_COPY_3_path, '5', '7', sbox.repo_url + '/A', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, check_props=True) svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive', wc_restricted) # Test issue #2997. If a merge requires two separate editor drives and the # first is non-operative we should still update the mergeinfo to reflect # this. # # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H. r5 gets merged first # but is a no-op, r8 get's merged next and is operative so the mergeinfo # should be updated on the merge target to reflect both merges. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), }) expected_elision_output = wc.State(A_COPY_2_H_path, { }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8'}), 'chi' : Item("This is the file 'chi'.\n"), }) expected_skip = wc.State(A_COPY_2_H_path, { 'psi' : Item(verb='Skipped missing target'), }) # Note we don't bother checking expected mergeinfo output because the # multiple merges being performed here, -c5 and -c8, will result in # first ' U' and then ' G' mergeinfo notifications. Our expected # tree structures can't handle checking for multiple values for the # same key. svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '5', sbox.repo_url + '/A/D/H', None, expected_output, None, # expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, [], True, False, '-c5', '-c8', A_COPY_2_H_path) # Test issue #2829 'Improve handling for skipped paths encountered # during a merge' # Revert previous changes to restricted WC svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive', wc_restricted) # Add new path 'A/D/H/zeta' svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n") svntest.actions.run_and_verify_svn(None, [], 'add', zeta_path) expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')}) wc_status.add({'A/D/H/zeta' : Item(status=' ', wc_rev=9)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status) # Merge -r7:9 to the restricted WC's A_COPY_2/D/H. # # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a missing sibling 'psi', # but since 'psi' is untouched by the merge it isn't skipped, and since it # isn't skipped, its parent 'A_COPY_2/D/H' won't get non-inheritable # mergeinfo set on it to describe the merge, so none of the parent's # children will get explicit mergeinfo -- see issue #4056. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), 'zeta' : Item(status='A '), }) expected_mergeinfo_output = wc.State(A_COPY_2_H_path, { '' : Item(status=' U'), 'omega' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status=' U'), }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='M ', wc_rev=8), 'zeta' : Item(status='A ', copied='+', wc_rev='-'), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9'}), 'omega' : Item("New content"), 'chi' : Item("This is the file 'chi'.\n"), 'zeta' : Item("This is the file 'zeta'.\n"), }) expected_skip = wc.State(A_COPY_2_H_path, {}) svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9', sbox.repo_url + '/A/D/H', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, check_props=True) # Merge -r4:9 to the restricted WC's A_COPY_2/D/H. # # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing # and skipped). 'A_COPY_2/D/H/zeta' must therefore get its own explicit # mergeinfo from this merge. svntest.actions.run_and_verify_svn(None, [], 'revert', '--recursive', wc_restricted) expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), 'zeta' : Item(status='A '), }) expected_mergeinfo_output = wc.State(A_COPY_2_H_path, { '' : Item(status=' U'), 'omega' : Item(status=' U'), 'zeta' : Item(status=' U'), }) expected_elision_output = wc.State(A_COPY_2_H_path, { }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' ', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), 'zeta' : Item(status='A ', copied='+', wc_rev='-'), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-9*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-9'}), 'chi' : Item("This is the file 'chi'.\n"), 'zeta' : Item("This is the file 'zeta'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:9'}), }) expected_skip = wc.State(A_COPY_2_H_path, { 'psi' : Item(verb='Skipped missing target'), }) svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '9', sbox.repo_url + '/A/D/H', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, check_props=True)
def reintegrate_fails_if_no_root_access(sbox): "reintegrate fails if no root access" # If a user is authorized to a reintegrate source and target, they # should be able to reintegrate, regardless of what authorization # they have to parents of the source and target. # # See http://subversion.tigris.org/issues/show_bug.cgi?id=3242#desc78 # Some paths we'll care about wc_dir = sbox.wc_dir A_path = sbox.ospath('A') A_COPY_path = sbox.ospath('A_COPY') beta_COPY_path = sbox.ospath('A_COPY/B/E/beta') rho_COPY_path = sbox.ospath('A_COPY/D/G/rho') omega_COPY_path = sbox.ospath('A_COPY/D/H/omega') psi_COPY_path = sbox.ospath('A_COPY/D/H/psi') # Copy A@1 to A_COPY in r2, and then make some changes to A in r3-6. sbox.build() wc_dir = sbox.wc_dir expected_disk, expected_status = set_up_branch(sbox) # Make a change on the branch, to A_COPY/mu, commit in r7. svntest.main.file_write(sbox.ospath("A_COPY/mu"), "Changed on the branch.") expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) expected_status.tweak('A_COPY/mu', wc_rev=7) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.') # Update the WC. svntest.main.run_svn(None, 'up', wc_dir) # Sync A_COPY with A. expected_output = expected_merge_output([[2,7]], ['U ' + beta_COPY_path + '\n', 'U ' + rho_COPY_path + '\n', 'U ' + omega_COPY_path + '\n', 'U ' + psi_COPY_path + '\n', # Mergeinfo notification ' U ' + A_COPY_path + '\n']) svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge', sbox.repo_url + '/A', A_COPY_path) svntest.main.run_svn(None, 'ci', '-m', 'synch A_COPY with A', wc_dir) # Update so we are ready for reintegrate. svntest.main.run_svn(None, 'up', wc_dir) # Change authz file so everybody has access to everything but the root. if is_ra_type_svn() or is_ra_type_dav(): write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : "* =", "/A" : "* = rw", "/A_COPY" : "* = rw", "/iota" : "* = rw"}) # Now reintegrate A_COPY back to A. The lack of access to the root of the # repository shouldn't be a problem. expected_output = wc.State(A_path, { 'mu' : Item(status='U '), }) expected_mergeinfo_output = wc.State(A_path, { '' : Item(status=' U'), }) expected_elision_output = wc.State(A_path, { }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}), 'B' : Item(), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("New content"), 'B/F' : Item(), 'mu' : Item("Changed on the branch."), 'C' : Item(), 'D' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/H' : Item(), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/omega' : Item("New content"), 'D/H/psi' : Item("New content"), }) expected_status = wc.State(A_path, { "B" : Item(status=' ', wc_rev=8), "B/lambda" : Item(status=' ', wc_rev=8), "B/E" : Item(status=' ', wc_rev=8), "B/E/alpha" : Item(status=' ', wc_rev=8), "B/E/beta" : Item(status=' ', wc_rev=8), "B/F" : Item(status=' ', wc_rev=8), "mu" : Item(status='M ', wc_rev=8), "C" : Item(status=' ', wc_rev=8), "D" : Item(status=' ', wc_rev=8), "D/gamma" : Item(status=' ', wc_rev=8), "D/G" : Item(status=' ', wc_rev=8), "D/G/pi" : Item(status=' ', wc_rev=8), "D/G/rho" : Item(status=' ', wc_rev=8), "D/G/tau" : Item(status=' ', wc_rev=8), "D/H" : Item(status=' ', wc_rev=8), "D/H/chi" : Item(status=' ', wc_rev=8), "D/H/omega" : Item(status=' ', wc_rev=8), "D/H/psi" : Item(status=' ', wc_rev=8), "" : Item(status=' M', wc_rev=8), }) expected_skip = wc.State(A_path, {}) svntest.actions.run_and_verify_merge(A_path, None, None, sbox.repo_url + '/A_COPY', None, expected_output, expected_mergeinfo_output, expected_elision_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, True, True, '--reintegrate', A_path)
def reintegrate_fails_if_no_root_access(sbox): "reintegrate fails if no root access" # If a user is authorized to a reintegrate source and target, they # should be able to reintegrate, regardless of what authorization # they have to parents of the source and target. # # See http://subversion.tigris.org/issues/show_bug.cgi?id=3242#desc78 # Some paths we'll care about wc_dir = sbox.wc_dir A_path = os.path.join(wc_dir, 'A') A_COPY_path = os.path.join(wc_dir, 'A_COPY') beta_COPY_path = os.path.join(wc_dir, 'A_COPY', 'B', 'E', 'beta') rho_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'G', 'rho') omega_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'omega') psi_COPY_path = os.path.join(wc_dir, 'A_COPY', 'D', 'H', 'psi') # Copy A@1 to A_COPY in r2, and then make some changes to A in r3-6. sbox.build() wc_dir = sbox.wc_dir expected_disk, expected_status = set_up_branch(sbox) # Make a change on the branch, to A_COPY/mu, commit in r7. svntest.main.file_write(os.path.join(wc_dir, "A_COPY", "mu"), "Changed on the branch.") expected_output = wc.State(wc_dir, {'A_COPY/mu' : Item(verb='Sending')}) expected_status.tweak('A_COPY/mu', wc_rev=7) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) expected_disk.tweak('A_COPY/mu', contents='Changed on the branch.') # Update the WC. svntest.main.run_svn(None, 'up', wc_dir) # Sync A_COPY with A. expected_output = expected_merge_output([[2,7]], ['U ' + beta_COPY_path + '\n', 'U ' + rho_COPY_path + '\n', 'U ' + omega_COPY_path + '\n', 'U ' + psi_COPY_path + '\n', # Mergeinfo notification ' U ' + A_COPY_path + '\n']) svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge', sbox.repo_url + '/A', A_COPY_path) svntest.main.run_svn(None, 'ci', '-m', 'synch A_COPY with A', wc_dir) # Update so we are ready for reintegrate. svntest.main.run_svn(None, 'up', wc_dir) # Change authz file so everybody has access to everything but the root. if is_ra_type_svn() or is_ra_type_dav(): write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : "* =", "/A" : "* = rw", "/A_COPY" : "* = rw", "/iota" : "* = rw"}) # Now reintegrate A_COPY back to A. The lack of access to the root of the # repository shouldn't be a problem. expected_output = wc.State(A_path, { 'mu' : Item(status='U '), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A_COPY:2-8'}), 'B' : Item(), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("New content"), 'B/F' : Item(), 'mu' : Item("Changed on the branch."), 'C' : Item(), 'D' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/H' : Item(), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/omega' : Item("New content"), 'D/H/psi' : Item("New content"), }) expected_status = wc.State(A_path, { "B" : Item(status=' ', wc_rev=8), "B/lambda" : Item(status=' ', wc_rev=8), "B/E" : Item(status=' ', wc_rev=8), "B/E/alpha" : Item(status=' ', wc_rev=8), "B/E/beta" : Item(status=' ', wc_rev=8), "B/F" : Item(status=' ', wc_rev=8), "mu" : Item(status='M ', wc_rev=8), "C" : Item(status=' ', wc_rev=8), "D" : Item(status=' ', wc_rev=8), "D/gamma" : Item(status=' ', wc_rev=8), "D/G" : Item(status=' ', wc_rev=8), "D/G/pi" : Item(status=' ', wc_rev=8), "D/G/rho" : Item(status=' ', wc_rev=8), "D/G/tau" : Item(status=' ', wc_rev=8), "D/H" : Item(status=' ', wc_rev=8), "D/H/chi" : Item(status=' ', wc_rev=8), "D/H/omega" : Item(status=' ', wc_rev=8), "D/H/psi" : Item(status=' ', wc_rev=8), "" : Item(status=' M', wc_rev=8), }) expected_skip = wc.State(A_path, {}) svntest.actions.run_and_verify_merge(A_path, None, None, sbox.repo_url + '/A_COPY', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, True, True, '--reintegrate')
def copy_with_mod_from_unreadable_dir(sbox): "verify copies with mods from unreadable dirs" sbox.build() # Make a copy of the B directory. svntest.actions.run_and_verify_svn(None, [], 'cp', sbox.wc_dir + '/A/B', sbox.wc_dir + '/A/P') # Set a property inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'pset', 'foo', 'bar', sbox.wc_dir + '/A/P/lambda') # Add a new directory and file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'mkdir', sbox.wc_dir + '/A/P/NEW-DIR') svntest.main.file_append(sbox.wc_dir + '/A/P/E/new-file', "bla bla") svntest.main.run_svn(None, 'add', sbox.wc_dir + '/A/P/E/new-file') # Delete a file inside the copied directory. svntest.actions.run_and_verify_svn(None, [], 'rm', sbox.wc_dir + '/A/P/E/beta') # Commit the copy-with-modification. svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir, '-m', 'log_msg') # Lock down the source repository. write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={ src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/new-file\n', ' A /A/P/F\n', ' A /A/P/NEW-DIR\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '2', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:12]) svntest.actions.run_and_verify_svn(['bar\n'], [], 'pget', 'foo', dest_sbox.repo_url + '/A/P/lambda')
def merge_fails_if_subtree_is_deleted_on_src(sbox): "merge fails if subtree is deleted on src" ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ## # Create a WC sbox.build() wc_dir = sbox.wc_dir if is_ra_type_svn() or is_ra_type_dav(): write_authz_file(sbox, {"/" : "* = rw", "/unrelated" : ("* =\n" + svntest.main.wc_author2 + " = rw")}) # Some paths we'll care about Acopy_path = os.path.join(wc_dir, 'A_copy') gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma') Acopy_gamma_path = os.path.join(wc_dir, 'A_copy', 'D', 'gamma') Acopy_D_path = os.path.join(wc_dir, 'A_copy', 'D') A_url = sbox.repo_url + '/A' Acopy_url = sbox.repo_url + '/A_copy' # Contents to be added to 'gamma' new_content = "line1\nline2\nline3\nline4\nline5\n" svntest.main.file_write(gamma_path, new_content) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status = svntest.actions.get_virginal_state(wc_dir, 1) expected_status.tweak('A/D/gamma', wc_rev=2) # Commit the new content svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) svntest.actions.run_and_verify_svn(None, None, [], 'cp', A_url, Acopy_url, '-m', 'create a new copy of A') # Update working copy svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir) svntest.main.file_substitute(gamma_path, "line1", "this is line1") # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Sending'), }) # Create expected status tree for commit expected_status.tweak(wc_rev=3) expected_status.tweak('A/D/gamma', wc_rev=4) expected_status.add({ 'A_copy' : Item(status=' ', wc_rev=3), 'A_copy/B' : Item(status=' ', wc_rev=3), 'A_copy/B/lambda' : Item(status=' ', wc_rev=3), 'A_copy/B/E' : Item(status=' ', wc_rev=3), 'A_copy/B/E/alpha': Item(status=' ', wc_rev=3), 'A_copy/B/E/beta' : Item(status=' ', wc_rev=3), 'A_copy/B/F' : Item(status=' ', wc_rev=3), 'A_copy/mu' : Item(status=' ', wc_rev=3), 'A_copy/C' : Item(status=' ', wc_rev=3), 'A_copy/D' : Item(status=' ', wc_rev=3), 'A_copy/D/gamma' : Item(status=' ', wc_rev=3), 'A_copy/D/G' : Item(status=' ', wc_rev=3), 'A_copy/D/G/pi' : Item(status=' ', wc_rev=3), 'A_copy/D/G/rho' : Item(status=' ', wc_rev=3), 'A_copy/D/G/tau' : Item(status=' ', wc_rev=3), 'A_copy/D/H' : Item(status=' ', wc_rev=3), 'A_copy/D/H/chi' : Item(status=' ', wc_rev=3), 'A_copy/D/H/omega': Item(status=' ', wc_rev=3), 'A_copy/D/H/psi' : Item(status=' ', wc_rev=3), }) svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir) # Delete A/D/gamma from working copy svntest.actions.run_and_verify_svn(None, None, [], 'delete', gamma_path) # Create expected output tree for commit expected_output = wc.State(wc_dir, { 'A/D/gamma' : Item(verb='Deleting'), }) expected_status.remove('A/D/gamma') svntest.actions.run_and_verify_commit(wc_dir, expected_output, expected_status, None, wc_dir, wc_dir) svntest.actions.run_and_verify_svn(None, expected_merge_output([[3,4]], 'U ' + Acopy_gamma_path + '\n'), [], 'merge', '-r1:4', A_url + '/D/gamma' + '@4', Acopy_gamma_path) # r6: create an empty (unreadable) commit. # Empty or unreadable revisions used to crash a svn 1.6+ client when # used with a 1.5 server: # http://svn.haxx.se/dev/archive-2009-04/0476.shtml svntest.main.run_svn(None, 'mkdir', sbox.repo_url + '/unrelated', '--username', svntest.main.wc_author2, '-m', 'creating a rev with no paths.') # This merge causes a tree conflict. Since the result of the previous # merge of A/D/gamma into A_copy/D has not yet been committed, it is # considered a local modification of A_Copy/D/gamma by the following # merge. A delete merged ontop of a modified file is a tree conflict. # See notes/tree-conflicts/detection.txt svntest.actions.run_and_verify_svn(None, expected_merge_output([[6], [3,6]], ['D ' + Acopy_gamma_path + '\n', 'C ' + Acopy_D_path + '\n']), [], 'merge', '-r1:6', '--force', A_url, Acopy_path)
def authz_read_access(sbox): "test authz for read operations" sbox.build(create_wc=False) root_url = sbox.repo_url A_url = root_url + '/A' B_url = A_url + '/B' C_url = A_url + '/C' E_url = B_url + '/E' mu_url = A_url + '/mu' iota_url = root_url + '/iota' lambda_url = B_url + '/lambda' alpha_url = E_url + '/alpha' D_url = A_url + '/D' G_url = D_url + '/G' pi_url = G_url + '/pi' H_url = D_url + '/H' chi_url = H_url + '/chi' if sbox.repo_url.startswith("http"): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" # create some folders with spaces in their names svntest.actions.run_and_verify_svn( None, None, [], 'mkdir', '-m', 'logmsg', B_url + '/folder with spaces', B_url + '/folder with spaces/empty folder') write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file( sbox, { "/": "* = r", "/A/B": "* =", "/A/D": "* = rw", "/A/D/G": ("* = rw\n" + svntest.main.wc_author + " ="), "/A/D/H": ("* = \n" + svntest.main.wc_author + " = rw"), "/A/B/folder with spaces": (svntest.main.wc_author + " = r") }) # read a remote file svntest.actions.run_and_verify_svn(None, ["This is the file 'iota'.\n"], [], 'cat', iota_url) # read a remote file, readably by user specific exception svntest.actions.run_and_verify_svn(None, ["This is the file 'chi'.\n"], [], 'cat', chi_url) # read a remote file, unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', lambda_url) # read a remote file, unreadable through recursion: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', alpha_url) # read a remote file, user specific authorization is ignored because * = rw svntest.actions.run_and_verify_svn(None, ["This is the file 'pi'.\n"], [], 'cat', pi_url) # open a remote folder(ls) svntest.actions.run_and_verify_svn("ls remote root folder", ["A/\n", "iota\n"], [], 'ls', root_url) # open a remote folder(ls), unreadable: should fail svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput, 'ls', B_url) # open a remote folder(ls) with spaces, should succeed svntest.actions.run_and_verify_svn( None, None, [], 'ls', B_url + '/folder with spaces/empty folder') # open a remote folder(ls), unreadable through recursion: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'ls', E_url) # copy a remote file svntest.actions.run_and_verify_svn(None, None, [], 'cp', iota_url, D_url, '-m', 'logmsg') # copy a remote file, source is unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', lambda_url, D_url) # copy a remote folder svntest.actions.run_and_verify_svn(None, None, [], 'cp', C_url, D_url, '-m', 'logmsg') # copy a remote folder, source is unreadable: should fail svntest.actions.run_and_verify_svn(None, None, expected_err, 'cp', '-m', 'logmsg', E_url, D_url)
def mergeinfo_and_skipped_paths(sbox): "skipped paths get overriding mergeinfo" # Test that we override the mergeinfo for child paths which weren't # actually merged because they were skipped. # # This test covers paths skipped because: # # 1) The source of a merge is inaccessible due to authz restrictions. # 2) Destination of merge is inaccessible due to authz restrictions. # 3) Source *and* destination of merge is inaccessible due to authz # restrictions. # 4) File path is versioned but is missing from disk due to OS deletion. # This isn't technically part of issue #2893 but we handle this case # and it didn't warrant its own test). # # Eventually we should also test(?): # # 5) Dir path is versioned but is missing from disk due to an OS deletion. sbox.build() wc_dir = sbox.wc_dir wc_disk, wc_status = set_up_branch(sbox, False, 3) # Create a restrictive authz where part of the merge source and part # of the target are inaccesible. write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw", # Make a directory in the merge source inaccessible. "/A/B/E" : svntest.main.wc_author + "=", # Make a file and dir in the merge destination # inaccessible. "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=", "/A_COPY_2/D/G" : svntest.main.wc_author + "=", # Make the source and destination inaccessible. "/A_COPY_3/B/E" : svntest.main.wc_author + "=", }) # Checkout just the branch under the newly restricted authz. wc_restricted = sbox.add_wc_path('restricted') svntest.actions.run_and_verify_svn(None, None, [], 'checkout', sbox.repo_url, wc_restricted) # Some paths we'll use in the second WC. A_COPY_path = os.path.join(wc_restricted, "A_COPY") A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2") A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H") A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3") omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega") zeta_path = os.path.join(wc_dir, "A", "D", "H", "zeta") # Restrict access to some more of the merge destination the # old fashioned way, delete it via the OS. ### TODO: Delete a versioned directory? os.remove(omega_path) # Merge r4:8 into the restricted WC's A_COPY. # # We expect A_COPY/B/E to be skipped because we can't access the source # and A_COPY/D/H/omega because it is missing. Since we have A_COPY/B/E # we should override it's inherited mergeinfo, giving it just what it # inherited from A_COPY before the merge. omega is missing, but since # it is a file we can record the fact that it is missing in its parent # directory A_COPY/D/H. expected_output = wc.State(A_COPY_path, { 'D/G/rho' : Item(status='U '), 'D/H/psi' : Item(status='U '), }) expected_status = wc.State(A_COPY_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status='M ', wc_rev=8), 'D/H/omega' : Item(status='!M', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/psi' : Item("New content"), 'D/H/chi' : Item("This is the file 'chi'.\n"), # 'D/H/omega' : run_and_verify_merge() doesn't support checking # the props on a missing path, so we do that # manually (see below). 'D/H' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'D/G' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_path, { 'B/E' : Item(), 'D/H/omega' : Item(), }) saved_cwd = os.getcwd() svntest.actions.run_and_verify_merge(A_COPY_path, '4', '8', sbox.repo_url + \ '/A', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1) # Manually check the props on A_COPY/D/H/omega. svntest.actions.run_and_verify_svn(None, ['\n'], [], 'pg', SVN_PROP_MERGEINFO, omega_path) # Merge r4:8 into the restricted WC's A_COPY_2. # # As before we expect A_COPY_2/B/E to be skipped because we can't access the # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have # access. # # After the merge the parents of the missing dest paths, A_COPY_2/D and # A_COPY_2/D/H get non-inheritable mergeinfo. Those parents children that # *are* present, A_COPY_2/D/gamma, A_COPY_2/D/H/chi, and A_COPY_2/D/H/omega # get their own mergeinfo. Note that A_COPY_2/D/H is both the parent of # a missing child and the sibling of missing child, but the former always # takes precedence in terms of getting *non*-inheritable mergeinfo. expected_output = wc.State(A_COPY_2_path, { 'D/G' : Item(status=' ', treeconflict='C'), 'D/H/omega' : Item(status='U '), }) expected_status = wc.State(A_COPY_2_path, { '' : Item(status=' M', wc_rev=8), 'D/G' : Item(status='! ', treeconflict='C'), 'D/H/chi' : Item(status=' M', wc_rev=8), 'D/H/omega' : Item(status='MM', wc_rev=8), 'D/H' : Item(status=' M', wc_rev=8), 'D/gamma' : Item(status=' M', wc_rev=8), 'D' : Item(status=' M', wc_rev=8), 'B/lambda' : Item(status=' ', wc_rev=8), 'B/E' : Item(status=' M', wc_rev=8), 'B/E/alpha' : Item(status=' ', wc_rev=8), 'B/E/beta' : Item(status=' ', wc_rev=8), 'B/F' : Item(status=' ', wc_rev=8), 'B' : Item(status=' ', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}), 'D/H/omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}), 'D/H/chi' : Item("This is the file 'chi'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}), 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}), 'D/gamma' : Item("This is the file 'gamma'.\n", props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}), 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}), 'B/lambda' : Item("This is the file 'lambda'.\n"), 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}), 'B/E/alpha' : Item("This is the file 'alpha'.\n"), 'B/E/beta' : Item("This is the file 'beta'.\n"), 'B/F' : Item(), 'B' : Item(), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_2_path, { 'B/E' : Item(), 'D/H/psi' : Item(), }) saved_cwd = os.getcwd() svntest.actions.run_and_verify_merge(A_COPY_2_path, '4', '8', sbox.repo_url + \ '/A', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) # Merge r5:7 into the restricted WC's A_COPY_3. # # Again A_COPY_3/B/E should be skipped, but because we can't access the # source *or* the destination we expect its parent A_COPY_3/B to get # non-inheritable mergeinfo and its two existing siblings, A_COPY_3/B/F # and A_COPY_3/B/lambda to get their own mergeinfo. expected_output = wc.State(A_COPY_3_path, { 'D/G/rho' : Item(status='U '), }) expected_status = wc.State(A_COPY_3_path, { '' : Item(status=' M', wc_rev=8), 'D/H/chi' : Item(status=' ', wc_rev=8), 'D/H/omega' : Item(status=' ', wc_rev=8), 'D/H/psi' : Item(status=' ', wc_rev=8), 'D/H' : Item(status=' ', wc_rev=8), 'D/gamma' : Item(status=' ', wc_rev=8), 'D' : Item(status=' ', wc_rev=8), 'D/G' : Item(status=' ', wc_rev=8), 'D/G/pi' : Item(status=' ', wc_rev=8), 'D/G/rho' : Item(status='M ', wc_rev=8), 'D/G/tau' : Item(status=' ', wc_rev=8), 'B/lambda' : Item(status=' M', wc_rev=8), 'B/F' : Item(status=' M', wc_rev=8), 'B' : Item(status=' M', wc_rev=8), 'mu' : Item(status=' ', wc_rev=8), 'C' : Item(status=' ', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}), 'D/H/omega' : Item("This is the file 'omega'.\n"), 'D/H/chi' : Item("This is the file 'chi'.\n"), 'D/H/psi' : Item("This is the file 'psi'.\n"), 'D/H' : Item(), 'D/gamma' : Item("This is the file 'gamma'.\n"), 'D' : Item(), 'D/G' : Item(), 'D/G/pi' : Item("This is the file 'pi'.\n"), 'D/G/rho' : Item("New content"), 'D/G/tau' : Item("This is the file 'tau'.\n"), 'B/lambda' : Item("This is the file 'lambda'.\n", props={SVN_PROP_MERGEINFO : '/A/B/lambda:6-7'}), 'B/F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:6-7'}), 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}), 'mu' : Item("This is the file 'mu'.\n"), 'C' : Item(), }) expected_skip = wc.State(A_COPY_3_path, {'B/E' : Item()}) saved_cwd = os.getcwd() svntest.actions.run_and_verify_merge(A_COPY_3_path, '5', '7', sbox.repo_url + \ '/A', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_restricted) # Test issue #2997. If a merge requires two separate editor drives and the # first is non-operative we should still update the mergeinfo to reflect # this. # # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H. r5 gets merged first # but is a no-op, r8 get's merged next and is operative so the mergeinfo # should be updated to reflect both merges. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' M', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5,8'}), 'chi' : Item("This is the file 'chi'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5,8'}), }) expected_skip = wc.State(A_COPY_2_H_path, { 'psi' : Item(), }) saved_cwd = os.getcwd() svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '4', '5', sbox.repo_url + \ '/A/D/H', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0, '-c5', '-c8') # Test issue #2829 'Improve handling for skipped paths encountered # during a merge' # Revert previous changes to restricted WC svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_restricted) # Add new path 'A/D/H/zeta' svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n") svntest.actions.run_and_verify_svn(None, None, [], 'add', zeta_path) expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')}) wc_status.add({'A/D/H/zeta' : Item(status=' ', wc_rev=9)}) svntest.actions.run_and_verify_commit(wc_dir, expected_output, wc_status, None, wc_dir) # Merge -r7:9 to the restricted WC's A_COPY_2/D/H. # # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing). # 'A_COPY_2/D/H/zeta' must therefore get its own explicit mergeinfo from # this merge. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), 'zeta' : Item(status='A '), }) expected_status = wc.State(A_COPY_2_H_path, { '' : Item(status=' M', wc_rev=8), 'chi' : Item(status=' M', wc_rev=8), 'omega' : Item(status='MM', wc_rev=8), 'zeta' : Item(status='A ', copied='+', wc_rev='-'), }) expected_disk = wc.State('', { '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9*'}), 'omega' : Item("New content", props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8-9'}), 'chi' : Item("This is the file 'chi'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8-9'}), 'zeta' : Item("This is the file 'zeta'.\n", props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:8-9'}), }) expected_skip = wc.State(A_COPY_2_H_path, {}) saved_cwd = os.getcwd() svntest.actions.run_and_verify_merge(A_COPY_2_H_path, '7', '9', sbox.repo_url + \ '/A/D/H', expected_output, expected_disk, expected_status, expected_skip, None, None, None, None, None, 1, 0)
def copy_from_unreadable_dir(sbox): "verify that copies from unreadable dirs work" sbox.build() B_url = sbox.repo_url + '/A/B' P_url = sbox.repo_url + '/A/P' # Set a property on the directory we're going to copy, and a file in it, to # confirm that they're transmitted when we later sync the copied directory svntest.actions.run_and_verify_svn(None, [], 'pset', 'foo', 'bar', sbox.wc_dir + '/A/B/lambda') svntest.actions.run_and_verify_svn(None, [], 'pset', 'baz', 'zot', sbox.wc_dir + '/A/B') svntest.actions.run_and_verify_svn(None, [], 'ci', sbox.wc_dir + '/A/B', '-m', 'log_msg') # Now copy that directory so we'll see it in our synced copy svntest.actions.run_and_verify_svn(None, [], 'cp', B_url, P_url, '-m', 'Copy B to P') write_restrictive_svnserve_conf(sbox.repo_dir) dest_sbox = sbox.clone_dependent() dest_sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(dest_sbox.repo_dir) src_authz = sbox.authz_name() dst_authz = dest_sbox.authz_name() write_authz_file(sbox, None, prefixed_rules={ src_authz + ':/': '* = r', src_authz + ':/A/B': '* =', dst_authz + ':/': '* = rw', }) run_init(dest_sbox.repo_url, sbox.repo_url) run_sync(dest_sbox.repo_url) expected_out = [ 'Changed paths:\n', ' A /A/P\n', ' A /A/P/E\n', ' A /A/P/E/alpha\n', ' A /A/P/E/beta\n', ' A /A/P/F\n', ' A /A/P/lambda\n', '\n', '\n', # log message is stripped ] exit_code, out, err = svntest.main.run_svn(None, 'log', '-r', '3', '-v', dest_sbox.repo_url) if err: raise SVNUnexpectedStderr(err) svntest.verify.compare_and_display_lines(None, 'LOG', expected_out, out[2:11]) svntest.actions.run_and_verify_svn(['bar\n'], [], 'pget', 'foo', dest_sbox.repo_url + '/A/P/lambda') svntest.actions.run_and_verify_svn(['zot\n'], [], 'pget', 'baz', dest_sbox.repo_url + '/A/P')
def authz_log_and_tracing_test(sbox): "test authz for log and tracing path changes" sbox.build() wc_dir = sbox.wc_dir write_restrictive_svnserve_conf(sbox.repo_dir) # write an authz file with *=rw on / if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" write_authz_file(sbox, { "/": "* = rw\n" }) root_url = sbox.repo_url D_url = root_url + '/A/D' G_url = D_url + '/G' # check if log doesn't spill any info on which you don't have read access rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') svntest.main.file_append(rho_path, 'new appended text for rho') svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'add file rho', sbox.wc_dir) svntest.main.file_append(rho_path, 'extra change in rho') svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', 'changed file rho', sbox.wc_dir) # copy a remote file svntest.actions.run_and_verify_svn(None, None, [], 'cp', rho_path, D_url, '-m', 'copy rho to readable area') # now disable read access on the first version of rho, keep the copy in # /A/D readable. if sbox.repo_url.startswith('http'): expected_err = ".*403 Forbidden.*" else: expected_err = ".*svn: Authorization failed.*" authz = { "/": "* = rw", "/A/D/G": "* ="} write_authz_file(sbox, authz) ## log # changed file in this rev. is not readable anymore, so author and date # should be hidden, like this: # r2 | (no author) | (no date) | 1 line svntest.actions.run_and_verify_svn(None, ".*(no author).*(no date).*|-+\n|\n", [], 'log', '-r', '2', '--limit', '1', wc_dir) if sbox.repo_url.startswith('http'): expected_err2 = expected_err else: expected_err2 = ".*svn: Item is not readable.*" # if we do the same thing directly on the unreadable file, we get: # svn: Item is not readable svntest.actions.run_and_verify_svn(None, None, expected_err2, 'log', rho_path) # while the HEAD rev of the copy is readable in /A/D, its parent in # /A/D/G is not, so don't spill any info there either. svntest.actions.run_and_verify_svn(None, ".*(no author).*(no date).*|-+\n|\n", [], 'log', '-r', '2', '--limit', '1', D_url) # Test that only author/date are shown for partially visible revisions. svntest.actions.enable_revprop_changes(sbox.repo_dir) write_authz_file(sbox, { "/": "* = rw"}) svntest.actions.run_and_verify_svn( None, None, [], # message, expected_stdout, expected_stderr 'ps', '--revprop', '-r1', 'foobar', 'foo bar', sbox.repo_url) svntest.actions.run_and_verify_log_xml( expected_revprops=[{'svn:author': svntest.main.wc_author, 'svn:date': '', 'svn:log': 'Log message for revision 1.', 'foobar': 'foo bar'}], args=['--with-all-revprops', '-r1', sbox.repo_url]) write_authz_file(sbox, authz) svntest.actions.run_and_verify_log_xml( expected_revprops=[{'svn:author': svntest.main.wc_author, 'svn:date': ''}], args=['--with-all-revprops', '-r1', sbox.repo_url]) ## cat # now see if we can look at the older version of rho svntest.actions.run_and_verify_svn(None, None, expected_err, 'cat', '-r', '2', D_url+'/rho') if sbox.repo_url.startswith('http'): expected_err2 = expected_err else: expected_err2 = ".*svn: Unreadable path encountered; access denied.*" svntest.actions.run_and_verify_svn(None, None, expected_err2, 'cat', '-r', '2', G_url+'/rho') ## diff # we shouldn't see the diff of a file in an unreadable path svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', 'HEAD', G_url+'/rho') svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', '2', D_url+'/rho') svntest.actions.run_and_verify_svn(None, None, expected_err, 'diff', '-r', '2:4', D_url+'/rho')